Introduction
The data properties are typically numerical or categorical values,
while the visual properties include the x and y positions of points,
colors of lines, heights of bars, and so on. The process of creating a
data visualization is to map the data properties to visual
properties.
In R’s base graphics functions, each mapping of data properties to
visual properties is its special case. Changing the mappings in the base
R graphics may require restructuring the data utilizing completely
different plotting commands, or both.
On the other hand, ggplot2 is a system for declaratively
creating graphics, based on The Grammar of Graphics. We provide the
data, and tell ggplot2 how to map variables to aesthetics and what
graphical primitives to use, ggplot() takes care of the
details.
The graphic functions in base R are powerful, but in general, it is
believed that ggplot() is better.
For those who program in Python, It is good to know that
plotnine is an implementation of a grammar of graphics in
Python, it is based on ggplot2().
For those who program in SAS, the SAS ODS graphics are roughly
analogous to R’s ggplot() although it is not a direct
implementation of The Grammar of Graphics.
Basics of
ggplot()
Plotting with ggplot2 is based on “adding” plot layers
and design elements on top of one another, with each command added to
the previous ones with a plus symbol (+). The result is a
multi-layer plot object that can be saved, modified, printed, exported,
etc.
ggplot() objects can be highly complex, but the basic
order of layers will usually look like this:
Begin with the baseline ggplot() command - this
“opens” the ggplot and allows subsequent functions to be added with
+. Typically the data set is also specified in this
command
Add “geom” layers - these functions visualize the
data as geometries (shapes), e.g. as a bar graph, line plot, scatter
plot, histogram (or a combination!). These functions all start with
geom_ as a prefix.
Add design elements to the plot such as axis labels, titles,
fonts, sizes, color schemes, legends, or axes rotation
We can check the tidyverse reference site for more details at https://ggplot2.tidyverse.org/reference/index.htm
A simple example of skeleton code is as follows. We will explain each
component in the code below.
# Plot data from my data columns as red points
ggplot(data = my_data) + # use the dataset "my_data"
geom_point( # add a layer of points (dots)
mapping = aes(x = col1, y = col2), # "map" data column to axes
color = "red") + # Other specifications for the geom
labs()+ # here you add titles, axes labels, etc.
theme() # here you adjust color, font, size etc # of non-data plot elements (axes,
# title, etc.)
In the following sections, we will detail each of the components in
the above code.
Structure of
ggplot()
The opening command of any ggplot2 plot is
ggplot(). This command simply creates a blank canvas upon
which to add layers. It “opens” the way for further layers to be added
with a + symbol.
Typically, the command ggplot() includes the
data = argument for the plot. This sets the default data
set to be used for subsequent layers of the plot.
This command will end with a + after its closing
parentheses. This leaves the command “open”. The ggplot
will only execute/appear when the full command includes a final layer
without a + at the end.
# This will create a plot that is a blank canvas
ggplot(data = linelist)
Geoms
The above code creates a blank canvas. We need to create geometries
(shapes) from our data (e.g. bar plots, histograms, scatter plots, box
plots).
This is done by adding layers of “geoms” to the initial
ggplot() command. Many ggplot2 functions
create “geoms”. Each of these functions begins with “geom_”, so we will
refer to them generically as geom_XXXX().
There are over 40 geoms in ggplot2 and many others created by fans.
View them at the ggplot2 gallery. Some common
geoms are listed below:
- Histograms -
geom_histogram()
- Bar charts -
geom_bar() or geom_col()
- Box plots -
geom_boxplot()
- Points (e.g. scatter plots) -
geom_point()
- Line graphs -
geom_line() or
geom_path()
- Trend lines -
geom_smooth()
We can display one or multiple geoms in one plot. Each
is added to previous ggplot2 commands with a
+, and they are plotted sequentially such that later
geoms are plotted on top of previous ones.
Mapping Data to
Plot
geom functions require mapping (assigning) columns in
the data to components of the plot like the axes, shape colors, shape
sizes, etc. The mappings must be wrapped in the aes()
function, so we would write something like
mapping = aes(x = col1, y = col2).
For example, in the following example using iris data,
Sepal Length is mapped to the x-axis, and Sepal Width is mapped to the
y-axis. After a +, the plotting commands continue. A shape is created
with the “geom” function geom_point().
ggplot(data = iris, mapping = aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point()

When creating a histogram, only one variable is used. See the
following example.
ggplot(data = iris, mapping = aes(x = Petal.Width)) +
geom_histogram(binwidth = 0.2)

Arranging Multiple
Grobs on the Same Page
In the above subsection, we create two graphs on two different pages.
Sometimes, we want to place two more graphs on the same page for
comparison purposes. In base R, we have graphic functions such as
par() and layout() to set up a layout for the
graphic page.
In this note, we introduce the library cowplot to
arrange multiple graphical objects (a.k.a grobs) on a
page.
## Name the two plots first and then call the two grobs in the layout function
## scatter plot
scatter = ggplot(data = iris, mapping = aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point()
## histogram
hist = ggplot(data = iris, mapping = aes(x = Petal.Width)) +
geom_histogram(binwidth = 0.2)
## use plot_grid() in {cowplot} to layout the two plots
plot_grid(scatter, hist, labels=c("A", "B"), ncol = 2, nrow = 1)

Plot Aesthetics
In ggplot terminology a plot “aesthetic” has a specific
meaning. It refers to colors, sizes, transparencies, placement, etc. of
the plotted data.
Not all geoms will have the same aesthetic options, but
many can be used by most geoms.
Here are some examples:
shape = Display a point with geom_point()
as a dot, star, triangle, or
square, etc.
fill = The interior color (e.g. of a bar or
boxplot)
color = The exterior line of a bar, boxplot, etc., or
the point color if using geom_point()
size = Size (e.g. line thickness, point size)
alpha = Transparency (1 = opaque, 0 = invisible)
binwidth = Width of histogram bins
width = Width of “bar plot” columns
linetype = Line type (e.g. solid,
dashed, dotted)
The aesthetics of plot objects can be assigned values in two
ways:
Assigned a static value (e.g. color = “blue”) to apply across all
plotted observations
Assigned to a column of the data (e.g. color = hospital) such
that the display of each observation depends on its value in that
column
We have already added binwidth to the above histogram. Next, we add
color to the histogram
# Change histogram plot line colors by groups
scatter01 <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width,
color = Species,
size = Petal.Width)) +
geom_point(alpha = 0.5)
# Overlaid histograms
hist01 <- ggplot(iris, aes(x = Petal.Width, color=Species)) +
geom_histogram(fill="navy",
alpha = 0.7,
position = "identity",
binwidth = 0.2)
## use plot_grid() in {cowplot} to lay out the two plots
plot_grid(scatter01, hist01, labels=c("A", "B"), ncol = 2, nrow = 1)

Labels in
ggplot()
Surely you will want to add or adjust the plot’s labels. These are
most easily done within the labs() function which is added
to the plot with + just as the geoms were.
Within labs() you can provide character strings to these
arguments:
x = and y =: The x-axis and y-axis
title (labels)
title =: The main plot title
subtitle =: The subtitle of the plot, in smaller
text below the title
caption =: The caption of the plot, in bottom-right
by default
Here is a plot we made earlier, but with nicer labels:
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width,
color = Species,
size = Petal.Width)) +
geom_point(alpha = 0.5) +
labs(
x = "Sepal Length",
y = "Sepal Width",
# label for legends
size = "Sepal Length:",
color = "Species:",
title = "Association between Sepal Length and Width",
subtitle = "This is a partial scatter plot",
caption = paste("Created on", Sys.Date())) +
theme_minimal() # minimal theme

Themes in
ggplot()
The theme system in ggplot() does not affect how the
data is rendered by geoms, or how it is transformed by
scales. Themes don’t change the perceptual properties of
the plot, but they do help you make the plot aesthetically pleasing or
match an existing style guide. Themes give us control over
things like fonts, ticks, panel stripes, and backgrounds.
In other words, when creating the plot we determine how the data is
displayed, and then after it has been created we can edit every detail
of the rendering, using the theming system.
The theming system is composed of four main components:
Theme elements specify the non-data elements that we can control.
For example,
plot.title controls the appearance of the plot
title;
axis.ticks.x controls the ticks on the x-axis;
legend.key.height controls the height of the keys in
the legend.
Each element is associated with an element function, which
describes the visual properties of the element. For example,
element_text() sets the font size, color, and face of text
elements like plot.title.
The theme() function which allows you to override
the default theme elements by calling element functions, like
theme(plot.title = element_text(colour = "red")).
Complete themes, like theme_grey() set all of the
theme elements to values designed to work together
harmoniously.
Here are some especially common theme() arguments. You will recognize
some patterns, such as appending .x or .y to apply the change only to
one axis.
To get the complete list of themes, run the following code
#theme_get()
To make sure the plot can stand alone, we need to provide the plot
with axes, legend labels, and title, and tweak the color scale for
appropriate colors.
# adding themes
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width,
color = Species,
size = Petal.Width)) +
geom_point(alpha = 0.5) +
labs(
x = "Sepal Length",
y = "Sepal Width",
# label for legends
size = "Sepal Length:",
color = "Species:",
title = "Association between Sepal Length and Width" ) +
theme_minimal() + # minimal theme
theme( # list of themes applied to the plot
# plot title features
# font family: c("sans", "serif", "mono")
# font face: c("plain", "bold", "italic", "bold.italic")
plot.title = element_text(face = "bold",
size = 12,
family = "sans",
color = "darkred",
hjust = 0.5), # left(0),right(1)
# Labels of axes
axis.title.x = element_text(color = "red",
face = "italic",
family = "serif",
hjust = 0.5),
axis.title.y = element_text(color = "blue",
face = "bold",
vjust = 0.5),
axis.ticks = element_line(color = "red",
size = 0.5),
axis.line = element_line(color = "darkblue",
size = 1,
linetype = "solid"),
# Axis tick marks
axis.text.x = element_text(face="plain",
color="purple",
size=11,
angle=45),
axis.text.y = element_text(face="plain",
color="orange",
size=11,
angle=90),
# Features of legend
legend.background = element_rect(fill = "white",
size = 0.1,
color = "darkgreen"),
legend.justification = c(0.9, 0.8),
legend.position = "bottom",
## Panel grid
panel.grid.major = element_line(color = "lightblue",
size = 0.1),
panel.grid.minor = element_blank()
)

Complete Components
of Theme
Themes are a powerful way to customize the
non-data components of the plots: i.e. titles, labels,
fonts, background, gridlines, and legends. To give our plots a
consistent customized look, we can define a theme function and call the
theme function in any ggplots.
The tidyverse official website provides a comprehensive
document on theme components in `ggplot``. https://ggplot2.tidyverse.org/reference/theme.html.
Numerous examples have illustrated how to use various theme
components.
We can define a theme function that can be reused to customize the
plots. For example, we define the following theme and use it in
different plots.
myplot.theme <- function() {
theme(
plot.title = element_text(face = "bold",
size = 12,
family = "sans",
color = "darkred",
hjust = 0.5), # left(0),right(1)
# add border 1)
panel.border = element_rect(colour = "blue",
fill = NA,
linetype = 2),
# color background 2)
panel.background = element_rect(fill = "aliceblue"),
# modify grid 3)
panel.grid.major.x = element_line(colour = "steelblue",
linetype = 3,
size = 0.5),
panel.grid.minor.x = element_blank(),
panel.grid.major.y = element_line(colour = "steelblue",
linetype = 3,
size = 0.5),
panel.grid.minor.y = element_blank(),
# modify text, axis and colour 4) and 5)
axis.text = element_text(colour = "steelblue",
face = "italic",
family = "Times New Roman"),
axis.title = element_text(colour = "steelblue",
family = "Times New Roman"),
axis.ticks = element_line(colour = "steelblue"),
# legend at the bottom 6)
legend.position = "bottom",
legend.key.size = unit(0.6, 'cm'), #change legend key size
legend.key.height = unit(0.6, 'cm'), #change legend key height
legend.key.width = unit(0.6, 'cm'), #change legend key width
legend.title = element_text(size=8), #change legend title font size
legend.text = element_text(size=8)) #change legend text font size
}
Now we use the above theme in the following scatter plots. Instead of
using the colors based on the value of species, we manually select
colors to encode the values of species. The following URL links to a PDF
document with colors in R. http://www.stat.columbia.edu/~tzheng/files/Rcolor.pdf
# Change histogram plot line colors by groups
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width,
color = factor(Species),
size = Petal.Width)) +
geom_point(alpha = 0.5) +
scale_color_manual(values=c("dodgerblue4", "darkolivegreen4","darkorchid3")) +
labs(
x = "Sepal Length",
y = "Sepal Width",
## Color and size of labels
size = "Sepal Length:",
color = "Species:",
title = "Association between Sepal Length and Width") +
myplot.theme()

Next, we plot a histogram using the same theme.
ggplot(iris, aes(x = Petal.Width, color=Species)) +
geom_histogram(fill="navy",
alpha = 0.3,
position = "identity",
binwidth = 0.2) +
scale_color_manual(values=c("dodgerblue4", "darkolivegreen4",
"darkorchid3")) +
labs(
x = "Petal Width",
color = "Species:",
title = "Distribution of Petal Width") +
myplot.theme()

Adding Annotations to
Graphics
To make the graphic more informative, sometimes we may want to add
annotations to the graphic. If we create a statistical and probabilistic
graphic, occasionally we need to add mathematical equations with Greek
letters to the graphics.
Adding Text
Annotation to Graphics
Adding Plain Text
to Graphics
To add plain text to graphics in ggplot, we use the function
annotate() with given coordinates. For example, the scatter
plot shows two separate groups.
# Change histogram plot line colors by groups
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width,
color = factor(Species),
size = Petal.Width)) +
geom_point(alpha = 0.5) +
scale_color_manual(values=c("dodgerblue4", "darkolivegreen4", "darkorchid3")) +
labs(
x = "Sepal Length",
y = "Sepal Width",
## color and size of labels
size = "Sepal Length:",
color = "Species:",
title = "Association between Sepal Length and Width") +
myplot.theme() +
annotate(geom="text",
x=7,
y=4.1,
label=paste("The distribution of Setosa is different",
"from that of Versicolor and Viginica", sep = "\n"),
color="red",
hjust = 0.5)

Several other alternatives we can use to add text to graphics created
using `ggplot``.
Passing Parameters
in Annotation
# Change histogram plot line colors by groups
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width,
color = factor(Species),
size = Petal.Width)) +
geom_point(alpha = 0.5) +
scale_color_manual(values=c("dodgerblue4", "darkolivegreen4", "darkorchid3")) +
labs(
x = "Sepal Length",
y = "Sepal Width",
## Color and size of labels
size = "Sepal Length:",
color = "Species:",
title = "Association between Sepal Length and Width") +
myplot.theme() +
annotate(geom="text" ,
x=7,
y=4.4,
label=paste("The Pearson correlation coefficient r = ",
round(cor(iris$Sepal.Length, iris$Sepal.Width),3)),
color = "blue")

The correlation coefficient between sepal width and sepal length is
calculated directly from the data and passed to the annotation in the
graphic. Note that we used a very handy and important graphic function
paste() when adding the annotation.
Adding Mathematical
Equations to Graphics
Mathematical expressions made with the text geoms using
parse = TRUE in ggplot2 have a format similar
to those made with plotmath() and expression()
in base R, except that they are stored as strings, rather than as
expression objects.
To mix regular text with expressions, use single quotes within double
quotes (or vice versa) to mark the plain-text parts. Each block of text
enclosed by the inner quotes is treated as a variable in a mathematical
expression.
Bear in mind that, in R’s syntax for mathematical expressions, we
can’t simply put a variable right next to another
without something else in between. To display two variables next to each
other, put a * operator between them. when *
is displayed in a graphic, it is treated as an invisible multiplication
sign (for a visible multiplication sign, use %*%):
x.axis <- seq(0, 20, length.out = 100)
y.axis <- (1/sqrt(2*pi)*3)*exp(-(x.axis-10)^2/(2*9))
normal.data = data.frame(x=x.axis , y=y.axis)
##
ggplot(normal.data, aes(x = x.axis, y = y.axis)) +
geom_line(color = "blue") +
coord_cartesian(ylim = c(0, 1.25), xlim=c(0,20)) +
labs(
x = "Normal Score",
y = "Normal Density",
title = "Normal Density Curve") +
annotate("text", x = 10, y = 0.2,
parse = TRUE, size = 4,
label = "'Function: ' * y==frac(1, sqrt(2*pi)* sigma) %*% e^{-(x- mu)^2/2}",
color = "red")

Adding Images to
Existing ggPlots
To embed a PNG image to an existing graph created by
ggplot, we need to use readPNG() in library
png to load the image to R and
getURLcontent() in the RCurl to insert the
image to the graph.
# caturl <- "https://stat553.s3.amazonaws.com/ggplot/cat.png"
caturl <- "https://raw.githubusercontent.com/pengdsci/sta553/main/ggplot/cat.png"
my_cat <- readPNG(getURLContent(caturl))
raster.cat <- as.raster(my_cat)
# Change histogram plot line colors by groups
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width,
color = factor(Species),
size = Petal.Width)) +
geom_point(alpha = 0.5) +
labs(
x = "Sepal Length",
y = "Sepal Width",
## Color and size of labels
size = "Sepal Length:",
color = "Species:",
title = "Association between Sepal Length and Width") +
myplot.theme() +
annotation_raster(raster.cat, 4, 4.85, 3.65, 4.5)

Removing Chart
Junks
We remove some unnecessary marks and channels from the chart via
theme: change the background color, grid, and plot title.
myplot.theme_new <- function() {
theme(
#ggplot margins
plot.margin = margin(t = 50, # Top margin
r = 30, # Right margin
b = 30, # Bottom margin
l = 30), # Left margin
## ggplot titles
plot.title = element_text(face = "bold",
size = 12,
family = "sans",
color = "navy",
hjust = 0.5,
margin=margin(0,0,30,0)), # left(0),right(1)
# add border 1)
panel.border = element_rect(colour = NA,
fill = NA,
linetype = 2),
# color background 2)
panel.background = element_rect(fill = "#f6f6f6"),
# modify grid 3)
panel.grid.major.x = element_line(colour = 'white',
linetype = 3,
size = 0.5),
panel.grid.minor.x = element_blank(),
panel.grid.major.y = element_line(colour = 'white',
linetype = 3,
size = 0.5),
panel.grid.minor.y = element_blank(),
# modify text, axis and colour 4) and 5)
axis.text = element_text(colour = "navy",
#face = "italic",
size = 7,
#family = "Times New Roman"
),
axis.title = element_text(colour = "navy",
size = 7,
#family = "Times New Roman"
),
axis.ticks = element_line(colour = "navy"),
# legend at the bottom 6)
legend.position = "bottom",
legend.key.size = unit(0.6, 'cm'), #change legend key size
legend.key.height = unit(0.6, 'cm'), #change legend key height
legend.key.width = unit(0.6, 'cm'), #change legend key width
#legend.title = element_text(size=8), #change legend title font size
legend.title=element_blank(), # remove all legend titles
legend.key = element_rect(fill = "white"),
#####
legend.text = element_text(size=8)) #change legend text font size
}
# Change histogram plot line colors by groups
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width,
color = factor(Species)), linetype = Species) +
geom_point(size = 2, alpha = 0.7) +
stat_smooth(method = lm, se=FALSE, size = 0.3) +
scale_color_manual(values=c("dodgerblue4", "darkolivegreen4", "darkorchid3")) +
labs(
x = "Sepal Length",
y = "Sepal Width",
## labels of color and size
#size = "Sepal Length",
#color = NA,
title = "Association between Sepal Length and Width") +
myplot.theme_new() +
annotate(geom="text" ,
x=6.8,
y=2,
label=paste("The Pearson correlation coefficient r = ",
round(cor(iris$Sepal.Length, iris$Sepal.Width),3)),
size = 2,
color = "navy") +
coord_fixed(1) ## This changes the aspect ratio of the graph

Aminated Graph with
gganimate()
gganimate() extends the grammar of graphics as
implemented by ggplot2 to include the description of
animation. It does this by providing a range of new grammar classes that
can be added to the plot object in order to customize how it should
change with time.
transition_*() defines how the data should be spread
out and how it relates to itself across time.
view_*() defines how the positional scales should
change along with the animation.
shadow_*() defines how data from other points in
time should be presented in the given point in time.
enter_*()/exit_*() defines how new data should
appear and how old data should disappear during the course of the
animation.
ease_aes() defines how different aesthetics should
be eased during transitions.
The logic behind the gganimate is to create a sequence
of images and then make a gif image. We need to write HTML to include
this gif in the RMarkdown document.
library(gapminder)
p <- ggplot(gapminder, aes(x = gdpPercap,
y=lifeExp,
size = pop,
colour = country)) +
geom_point(aes(size = pop, ids = country ),
show.legend = FALSE,
alpha = 0.7) +
scale_color_viridis_d() + # color pallets
scale_size(range = c(2, 12)) +
scale_x_log10() +
labs(x = "GDP per capita",
y = "Life expectancy") +
## gganimate command
transition_time(year)
##
anim_save("LifeExp.gif", p)
# animate(p, renderer = gifski_renderer()) # This command will pop up a new graphic window showing the animation.
Since the gif image is made of individual static images, it is
different from the interactive plot presented in the previous sections
that have the capability of showing mode information of the data via
hover message.
The next gif graph consists of 5 panels, each representing a
continent. They are also fig images. Therefore, no hover message is
available for these gif figures.
We use the {gifki} package to render the images in the form of gif
and then include the gif image into the RMarkdown document directly.
w <- ggplot(gapminder, aes(gdpPercap, lifeExp,
size = pop, colour = country)) +
geom_point(alpha = 0.7, show.legend = FALSE) +
scale_colour_manual(values = country_colors) +
#scale_color_manual(values=c("dodgerblue4", "darkolivegreen4","darkorchid3")) +
#scale_color_brewer(palette="Set1") +
scale_size(range = c(2, 12)) +
scale_x_log10() +
# break down the previous single plot by continent
# facet_wrap(~continent) + # create multiple panels according to the continents
# Here comes the gganimate specific bits
labs(title = 'Year: {frame_time}',
x = 'GDP per capita',
y = 'life expectancy') +
transition_time(year) +
ease_aes('linear')
###
animate(w, renderer = gifski_renderer(),
rewind = TRUE)

The above code does not save the generated gif image to the document
folder (directory). If need to save it from the viewer window to the
designated folder and then embed it to a web page created by tools other
than the RMarkdown.
<br>
<center><img src="https://raw.githubusercontent.com/pengdsci/sta553/main/ggplot/LifeExpRewind.gif" alt="Life Expectancy Animation Rewind" height="500" width="400"></center>
<br>
Next, we create a group gif using facet_wrap() function. The code is
the same as the above example except for one additional function
call.
w <- ggplot(gapminder, aes(gdpPercap, lifeExp,
size = pop, colour = country)) +
geom_point(alpha = 0.7, show.legend = FALSE) +
scale_colour_manual(values = country_colors) +
#scale_color_manual(values=c("dodgerblue4", "darkolivegreen4","darkorchid3")) +
#scale_color_brewer(palette="Set1") +
scale_size(range = c(2, 12)) +
scale_x_log10() +
# break down the previous single plot by continent
facet_wrap(~continent) + # create multiple panels according to the continents
# Here comes the gganimate-specific bits
labs(title = 'Year: {frame_time}',
x = 'GDP per capita',
y = 'life expectancy') +
transition_time(year) +
ease_aes('linear')
###
animate(w, renderer = gifski_renderer(),
rewind = TRUE)

Ridgetline Plot with
ggridges Library
The ridgeline plot is a useful 3D to compare multiple densities. It
creates a 3D impression and has gained increasing popularity. Here we
use the California Housing Data that is available on the Project Data
Set https://pengdsci.github.io/datasets/#cal-housing.
CalHousing = read.csv("https://raw.githubusercontent.com/pengdsci/sta553.html/main/data/ca-housing-price.csv")
ggplot(CalHousing, aes(x = median_house_value, y = ocean_proximity, fill = ocean_proximity)) +
geom_density_ridges()

You can pass stat(x) or factor(stat(x)) to
the fill argument of aes and use
geom_density_ridges_gradient and a continuous fill color
scale to fill each ridgeline with a gradient.
ggplot(CalHousing, aes(x = median_house_value, y = ocean_proximity, fill = stat(x))) +
geom_density_ridges_gradient(jittered_points = TRUE,
position = position_points_jitter(width = 0.05, height = 0),
point_shape = '|', point_size = 1, point_alpha = 1, alpha = 0.3,) +
scale_fill_viridis_c(name = "median_house_value", option = "C")

Next, we explore the distribution of continuous variables in the iris
data set. As an example, we make the following ridgeline plot to see the
distribution of sepal widths across the species.
ggplot(iris, aes(x = Sepal.Width, y = Species, fill = stat(x))) +
geom_density_ridges_gradient(jittered_points = TRUE,
position = position_points_jitter(width = 0.05, height = 0),
point_shape = '|', point_size = 1, point_alpha = 1, alpha = 0.3,) +
scale_fill_viridis_c(name = "Sepal Width", option = "C")

The above distributions have similar shapes (variations) but with
different means. This also indicates the ANOVA model between sepal width
and species is appropriate.
Other Extensions to
ggplot
We have used ggplot extensions {gganimate} to create
animated graphs and {ggridges} to create ridgeline
graphs to compare multiple densities. There are several other important
ggplot extensions that enhance the basic ggplots.
ggdendro - controls the appearance and display of your cluster
analyses
ggthemes - contains themes and scales that enhance the standard
ggplots.
ggpubr - makes it easy to produce publication-ready plots using
ggplot.
Plotly - brings interactivity to ggplots. We will spend a week on
plotly().
patchwork - arranges multiple R plots on the same graphics
page
ggmap - is a powerful package for visualizing spatial data and
models. It layers data on top of static maps from popular online
sources. We will use these packages to make maps later.
ggrepel - gives ggplot2 users greater control over how text
labels appear in their charts.
ggcorrplot - controls the appearance of the matrix, from altering
the color, shape, or size of the boxes (as in the circle-matrix above),
to adding coefficient labels, reordering the matrix according to
hierarchical clustering, and so on.
GGally - brings together many useful additional visualization
functionality, all in one package.
ggiraph -is an htmlwidget that can be extended to an existing
ggplot2 such as bar chart, scatterplot, boxplot, map, etc., and does
things like displaying a tooltip of your choice.
Save
ggplot Images
A ggplot can be saved to different file formats,
including PDF, SVG vector files, PNG, TIFF, JPEG, etc.
We can either print directly a ggplot into
PNG/PDF files or use the convenient function
ggsave() for saving a ggplot.
The default of ggsave() is to export the last plot that
you displayed, using the size of the current graphics device. It also
guesses the type of graphics device from the extension.
General Steps
The standard procedure to save any graphics from R is as follows:
Open a graphic device using one of the following functions:
pdf(“r-graphics.pdf”),
svg(“r-graphics.svg”),
png(“r-graphics.png”),
tiff(“r-graphics.tiff”),
jpeg(“r-graphics.jpg”), etc.
Additional arguments indicating the width and the height (in
inches) of the graphics region can be also specified in the mentioned
function.
Create and print a plot. Close the graphic device using the
function dev.off().
Save
ggplot into a PDF File
The following code illustrates how to save a ggplot in a folder in
PDF format.
# scatter plots
iris.scatter <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
geom_point()
## box-plot
iris.boxplot <- ggplot(iris, aes(Species, Sepal.Length)) +
geom_boxplot()
# Print plots to a PDF file: one page per PDF file
pdf("savePDFggplot.pdf") # Save the PDF file in ggplot folder.
print(iris.scatter) # Plot 1 --> in the first page of PDF
print(iris.boxplot) # Plot 2 ---> in the second page of the PDF
dev.off()
## png
## 2
Save ggplot with
ggsave()
It’s also possible to make a ggplot and save it from the screen using
the function ggsave().
# 1. Create a plot: displayed on the screen (by default)
ggplot(mtcars, aes(wt, mpg)) + geom_point()

# 2.1. Save the plot to a pdf
ggsave("mtcarmyplot.pdf")
# 2.2 OR save it to png file
ggsave("mtcarmyplot.png")
We can also save multiple plots in the sample format to a single
file. We can use plot_grid() in {cowplot}
to make two figures on the same graphic page and then use
ggsave() to save it to a single file.
#
p1 <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
p2 <- ggplot(mtcars, aes(wt)) + geom_histogram()
combinedPlot <- plot_grid(p1, p2, labels=c("A", "B"),
ncol = 2, nrow = 1)
##
ggsave("CombinedPlot.png", plot = combinedPlot)
LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byBnZ3Bsb3QoKSINCmF1dGhvcjogIkNoZW5nIFBlbmciDQpkYXRlOiAiV2VzdCBDaGVzdGVyIFVuaXZlcnNpdHkgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIHRvY19mbG9hdDogeWVzDQogICAgZmlnX3dpZHRoOiA2DQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgZmlnX2hlaWdodDogNA0KLS0tDQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCmRpdiNUT0MgbGkgew0KICAgIGxpc3Qtc3R5bGU6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLWNvbG9yOmxpZ2h0Z3JheTsNCiAgICBiYWNrZ3JvdW5kLWltYWdlOm5vbmU7DQogICAgYmFja2dyb3VuZC1yZXBlYXQ6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOjA7DQogICAgZm9udC1mYW1pbHk6IEFyaWFsLCBIZWx2ZXRpY2EsIHNhbnMtc2VyaWY7DQogICAgY29sb3I6ICM3ODBjMGM7DQp9DQoNCi8qIG1vdXNlIG92ZXIgbGluayAqLw0KZGl2I1RPQyBhOmhvdmVyIHsNCiAgY29sb3I6IHJlZDsNCn0NCg0KLyogdW52aXNpdGVkIGxpbmsgKi8NCmRpdiNUT0MgYTpsaW5rIHsNCiAgY29sb3I6IGJsdWU7DQp9DQoNCg0KDQpoMS50aXRsZSB7DQogIGZvbnQtc2l6ZTogMjRweDsNCiAgY29sb3I6IERhcmtibHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiBBcmlhbCwgSGVsdmV0aWNhLCBzYW5zLXNlcmlmOw0KICBmb250LXZhcmlhbnQtY2Fwczogbm9ybWFsOw0KfQ0KaDQuYXV0aG9yIHsgDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmg0LmRhdGUgeyANCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoMSB7DQogICAgZm9udC1zaXplOiAyMnB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmgyIHsgLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoMyB7IA0KICAgIGZvbnQtc2l6ZTogMTVweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCi8qIHVudmlzaXRlZCBsaW5rICovDQphOmxpbmsgew0KICBjb2xvcjogZ3JlZW47DQp9DQoNCi8qIHZpc2l0ZWQgbGluayAqLw0KYTp2aXNpdGVkIHsNCiAgY29sb3I6IGdyZWVuOw0KfQ0KDQovKiBtb3VzZSBvdmVyIGxpbmsgKi8NCmE6aG92ZXIgew0KICBjb2xvcjogcmVkOw0KfQ0KDQovKiBzZWxlY3RlZCBsaW5rICovDQphOmFjdGl2ZSB7DQogIGNvbG9yOiB5ZWxsb3c7DQp9DQoNCjwvc3R5bGU+DQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgDQojIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dCBmaWxlcy4NCm9wdGlvbnMocmVwb3MgPSBsaXN0KENSQU49Imh0dHA6Ly9jcmFuLnJzdHVkaW8uY29tLyIpKQ0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCiAgIGxpYnJhcnkodGlkeXZlcnNlKQ0KfQ0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQogICBsaWJyYXJ5KGtuaXRyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJjb3dwbG90IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImNvd3Bsb3QiKQ0KICAgbGlicmFyeShjb3dwbG90KQ0KfQ0KaWYgKCFyZXF1aXJlKCJsYXRleDJleHAiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygibGF0ZXgyZXhwIikNCiAgIGxpYnJhcnkobGF0ZXgyZXhwKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwbG90bHkiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikNCiAgIGxpYnJhcnkocGxvdGx5KQ0KfQ0KaWYgKCFyZXF1aXJlKCJnYXBtaW5kZXIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2FwbWluZGVyIikNCiAgIGxpYnJhcnkoZ2FwbWluZGVyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwbmciKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInBuZyIpICAgICAgICAgICAgICMgSW5zdGFsbCBwbmcgcGFja2FnZQ0KICAgIGxpYnJhcnkoInBuZyIpDQp9DQppZiAoIXJlcXVpcmUoIlJDdXJsIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJSQ3VybCIpICAgICAgICAgICAjIEluc3RhbGwgUkN1cmwgcGFja2FnZQ0KICAgIGxpYnJhcnkoIlJDdXJsIikNCn0NCmlmICghcmVxdWlyZSgiY29sb3VycGlja2VyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJjb2xvdXJwaWNrZXIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiY29sb3VycGlja2VyIikNCn0NCmlmICghcmVxdWlyZSgiZ2lmc2tpIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnaWZza2kiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ2lmc2tpIikNCn0NCmlmICghcmVxdWlyZSgibWFnaWNrIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJtYWdpY2siKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgibWFnaWNrIikNCn0NCmlmICghcmVxdWlyZSgiZ3JEZXZpY2VzIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnckRldmljZXMiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ3JEZXZpY2VzIikNCn0NCiMjIyBnZ3Bsb3QgYW5kIGV4dGVuc2lvbnMNCmlmICghcmVxdWlyZSgiZ2dwbG90MiIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnZ3Bsb3QyIikNCn0NCmlmICghcmVxdWlyZSgiZ2dhbmltYXRlIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnZ2FuaW1hdGUiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ2dhbmltYXRlIikNCn0NCmlmICghcmVxdWlyZSgiZ2dyaWRnZXMiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdncmlkZ2VzIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdncmlkZ2VzIikNCn0NCiMga25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAiQzovVXNlcnMvNzVDUEVORy9PbmVEcml2ZSAtIFdlc3QgQ2hlc3RlciBVbml2ZXJzaXR5IG9mIFBBL0RvY3VtZW50cyIpDQojIGtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIkM6XFxTVEE0OTBcXHcwNSIpDQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IFRSVUUsICAgDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFKQ0KYGBgDQoNCg0KXA0KDQoNCiMgSW50cm9kdWN0aW9uDQoNClRoZSBkYXRhIHByb3BlcnRpZXMgYXJlIHR5cGljYWxseSBudW1lcmljYWwgb3IgY2F0ZWdvcmljYWwgdmFsdWVzLCB3aGlsZSB0aGUgdmlzdWFsIHByb3BlcnRpZXMgaW5jbHVkZSB0aGUgeCBhbmQgeSBwb3NpdGlvbnMgb2YgcG9pbnRzLCBjb2xvcnMgb2YgbGluZXMsIGhlaWdodHMgb2YgYmFycywgYW5kIHNvIG9uLiBUaGUgcHJvY2VzcyBvZiBjcmVhdGluZyBhIGRhdGEgdmlzdWFsaXphdGlvbiBpcyB0byBtYXAgdGhlIGRhdGEgcHJvcGVydGllcyB0byB2aXN1YWwgcHJvcGVydGllcy4NCg0KSW4gUuKAmXMgYmFzZSBncmFwaGljcyBmdW5jdGlvbnMsIGVhY2ggbWFwcGluZyBvZiBkYXRhIHByb3BlcnRpZXMgdG8gdmlzdWFsIHByb3BlcnRpZXMgaXMgaXRzIHNwZWNpYWwgY2FzZS4gQ2hhbmdpbmcgdGhlIG1hcHBpbmdzIGluIHRoZSBiYXNlIFIgZ3JhcGhpY3MgbWF5IHJlcXVpcmUgcmVzdHJ1Y3R1cmluZyB0aGUgZGF0YSB1dGlsaXppbmcgY29tcGxldGVseSBkaWZmZXJlbnQgcGxvdHRpbmcgY29tbWFuZHMsIG9yIGJvdGguDQogDQpPbiB0aGUgb3RoZXIgaGFuZCwgYGdncGxvdDJgIGlzIGEgc3lzdGVtIGZvciBkZWNsYXJhdGl2ZWx5IGNyZWF0aW5nIGdyYXBoaWNzLCBiYXNlZCBvbiBUaGUgR3JhbW1hciBvZiBHcmFwaGljcy4gV2UgcHJvdmlkZSB0aGUgZGF0YSwgYW5kIHRlbGwgZ2dwbG90MiBob3cgdG8gbWFwIHZhcmlhYmxlcyB0byBhZXN0aGV0aWNzIGFuZCB3aGF0IGdyYXBoaWNhbCBwcmltaXRpdmVzIHRvIHVzZSwgYGdncGxvdCgpYCB0YWtlcyBjYXJlIG9mIHRoZSBkZXRhaWxzLg0KDQpUaGUgZ3JhcGhpYyBmdW5jdGlvbnMgaW4gYmFzZSBSIGFyZSBwb3dlcmZ1bCwgYnV0IGluIGdlbmVyYWwsIGl0IGlzIGJlbGlldmVkIHRoYXQgYGdncGxvdCgpYCBpcyBiZXR0ZXIuIA0KDQpGb3IgdGhvc2Ugd2hvIHByb2dyYW0gaW4gUHl0aG9uLCBJdCBpcyBnb29kIHRvIGtub3cgdGhhdCBgcGxvdG5pbmVgIGlzIGFuIGltcGxlbWVudGF0aW9uIG9mIGEgZ3JhbW1hciBvZiBncmFwaGljcyBpbiAqKlB5dGhvbioqLCBpdCBpcyBiYXNlZCBvbiBgZ2dwbG90MigpYC4NCg0KRm9yIHRob3NlIHdobyBwcm9ncmFtIGluIFNBUywgdGhlIFNBUyBPRFMgZ3JhcGhpY3MgYXJlIHJvdWdobHkgYW5hbG9nb3VzIHRvIFIncyBgZ2dwbG90KClgIGFsdGhvdWdoIGl0IGlzIG5vdCBhIGRpcmVjdCBpbXBsZW1lbnRhdGlvbiBvZiBUaGUgR3JhbW1hciBvZiBHcmFwaGljcy4NCg0KDQoNCg0KIyBCYXNpY3Mgb2YgYGdncGxvdCgpYA0KDQpQbG90dGluZyB3aXRoIGBnZ3Bsb3QyYCBpcyBiYXNlZCBvbiDigJxhZGRpbmfigJ0gcGxvdCBsYXllcnMgYW5kIGRlc2lnbiBlbGVtZW50cyBvbiB0b3Agb2Ygb25lIGFub3RoZXIsIHdpdGggZWFjaCBjb21tYW5kIGFkZGVkIHRvIHRoZSBwcmV2aW91cyBvbmVzIHdpdGggYSBwbHVzIHN5bWJvbCAoYCtgKS4gVGhlIHJlc3VsdCBpcyBhIG11bHRpLWxheWVyIHBsb3Qgb2JqZWN0IHRoYXQgY2FuIGJlIHNhdmVkLCBtb2RpZmllZCwgcHJpbnRlZCwgZXhwb3J0ZWQsIGV0Yy4NCg0KYGdncGxvdCgpYCBvYmplY3RzIGNhbiBiZSBoaWdobHkgY29tcGxleCwgYnV0IHRoZSBiYXNpYyBvcmRlciBvZiBsYXllcnMgd2lsbCB1c3VhbGx5IGxvb2sgbGlrZSB0aGlzOg0KDQoxLiBCZWdpbiB3aXRoIHRoZSBiYXNlbGluZSBgZ2dwbG90KClgIGNvbW1hbmQgLSB0aGlzIOKAnG9wZW5z4oCdIHRoZSBnZ3Bsb3QgYW5kIGFsbG93cyBzdWJzZXF1ZW50IGZ1bmN0aW9ucyB0byBiZSBhZGRlZCB3aXRoIGArYC4gVHlwaWNhbGx5IHRoZSBkYXRhIHNldCBpcyBhbHNvIHNwZWNpZmllZCBpbiB0aGlzIGNvbW1hbmQNCg0KMi4gQWRkIGDigJxnZW9t4oCdYCBsYXllcnMgLSB0aGVzZSBmdW5jdGlvbnMgdmlzdWFsaXplIHRoZSBkYXRhIGFzIGdlb21ldHJpZXMgKHNoYXBlcyksIGUuZy4gYXMgYSBiYXIgZ3JhcGgsIGxpbmUgcGxvdCwgc2NhdHRlciBwbG90LCBoaXN0b2dyYW0gKG9yIGEgY29tYmluYXRpb24hKS4gVGhlc2UgZnVuY3Rpb25zIGFsbCBzdGFydCB3aXRoIGBnZW9tX2AgYXMgYSBwcmVmaXguDQoNCjMuIEFkZCBkZXNpZ24gZWxlbWVudHMgdG8gdGhlIHBsb3Qgc3VjaCBhcyBheGlzIGxhYmVscywgdGl0bGVzLCBmb250cywgc2l6ZXMsIGNvbG9yIHNjaGVtZXMsIGxlZ2VuZHMsIG9yIGF4ZXMgcm90YXRpb24NCg0KV2UgY2FuIGNoZWNrIHRoZSB0aWR5dmVyc2UgcmVmZXJlbmNlIHNpdGUgZm9yIG1vcmUgZGV0YWlscyBhdCA8aHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2luZGV4Lmh0bT4NCg0KDQoNCkEgc2ltcGxlIGV4YW1wbGUgb2Ygc2tlbGV0b24gY29kZSBpcyBhcyBmb2xsb3dzLiBXZSB3aWxsIGV4cGxhaW4gZWFjaCBjb21wb25lbnQgaW4gdGhlIGNvZGUgYmVsb3cuDQoNCmBgYHt9DQojIFBsb3QgZGF0YSBmcm9tIG15IGRhdGEgY29sdW1ucyBhcyByZWQgcG9pbnRzDQpnZ3Bsb3QoZGF0YSA9IG15X2RhdGEpICArICAgICAgICAgICAgICAgIyB1c2UgdGhlIGRhdGFzZXQgIm15X2RhdGEiDQogIGdlb21fcG9pbnQoICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBhZGQgYSBsYXllciBvZiBwb2ludHMgKGRvdHMpDQogICAgbWFwcGluZyA9IGFlcyh4ID0gY29sMSwgeSA9IGNvbDIpLCAgIyAibWFwIiBkYXRhIGNvbHVtbiB0byBheGVzDQogICAgY29sb3IgPSAicmVkIikgICsgICAgICAgICAgICAgICAgICAgIyBPdGhlciBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGdlb20NCiAgbGFicygpKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGhlcmUgeW91IGFkZCB0aXRsZXMsIGF4ZXMgbGFiZWxzLCBldGMuDQogIHRoZW1lKCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBoZXJlIHlvdSBhZGp1c3QgY29sb3IsIGZvbnQsIHNpemUgZXRjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG9mIG5vbi1kYXRhIHBsb3QgZWxlbWVudHMgKGF4ZXMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGl0bGUsIGV0Yy4pIA0KYGBgDQoNCkluIHRoZSBmb2xsb3dpbmcgc2VjdGlvbnMsIHdlIHdpbGwgZGV0YWlsIGVhY2ggb2YgdGhlIGNvbXBvbmVudHMgaW4gdGhlIGFib3ZlIGNvZGUuDQoNCg0KIyBTdHJ1Y3R1cmUgb2YgYGdncGxvdCgpYA0KDQpUaGUgb3BlbmluZyBjb21tYW5kIG9mIGFueSBgZ2dwbG90MmAgcGxvdCBpcyBgZ2dwbG90KClgLiBUaGlzIGNvbW1hbmQgc2ltcGx5IGNyZWF0ZXMgYSBibGFuayBjYW52YXMgdXBvbiB3aGljaCB0byBhZGQgbGF5ZXJzLiBJdCDigJxvcGVuc+KAnSB0aGUgd2F5IGZvciBmdXJ0aGVyIGxheWVycyB0byBiZSBhZGRlZCB3aXRoIGEgYCtgIHN5bWJvbC4NCg0KVHlwaWNhbGx5LCB0aGUgY29tbWFuZCBnZ3Bsb3QoKSBpbmNsdWRlcyB0aGUgYGRhdGEgPSBhcmd1bWVudGAgZm9yIHRoZSBwbG90LiBUaGlzIHNldHMgdGhlIGRlZmF1bHQgZGF0YSBzZXQgdG8gYmUgdXNlZCBmb3Igc3Vic2VxdWVudCBsYXllcnMgb2YgdGhlIHBsb3QuDQoNClRoaXMgY29tbWFuZCB3aWxsIGVuZCB3aXRoIGEgYCtgIGFmdGVyIGl0cyBjbG9zaW5nIHBhcmVudGhlc2VzLiBUaGlzIGxlYXZlcyB0aGUgY29tbWFuZCDigJxvcGVu4oCdLiBUaGUgYGdncGxvdGAgd2lsbCBvbmx5IGV4ZWN1dGUvYXBwZWFyIHdoZW4gdGhlIGZ1bGwgY29tbWFuZCBpbmNsdWRlcyBhIGZpbmFsIGxheWVyICoqd2l0aG91dCoqIGEgYCtgIGF0IHRoZSBlbmQuDQoNCmBgYHt9DQojIFRoaXMgd2lsbCBjcmVhdGUgYSBwbG90IHRoYXQgaXMgYSBibGFuayBjYW52YXMNCmdncGxvdChkYXRhID0gbGluZWxpc3QpDQpgYGANCg0KIyBHZW9tcw0KDQpUaGUgYWJvdmUgY29kZSBjcmVhdGVzIGEgYmxhbmsgY2FudmFzLiBXZSBuZWVkIHRvIGNyZWF0ZSBnZW9tZXRyaWVzIChzaGFwZXMpIGZyb20gb3VyIGRhdGEgKGUuZy4gYmFyIHBsb3RzLCBoaXN0b2dyYW1zLCBzY2F0dGVyIHBsb3RzLCBib3ggcGxvdHMpLg0KDQpUaGlzIGlzIGRvbmUgYnkgYWRkaW5nIGxheWVycyBvZiDigJxnZW9tc+KAnSB0byB0aGUgaW5pdGlhbCBgZ2dwbG90KClgIGNvbW1hbmQuIE1hbnkgYGdncGxvdDJgIGZ1bmN0aW9ucyBjcmVhdGUg4oCcZ2VvbXPigJ0uIEVhY2ggb2YgdGhlc2UgZnVuY3Rpb25zIGJlZ2lucyB3aXRoIOKAnGdlb21f4oCdLCBzbyB3ZSB3aWxsIHJlZmVyIHRvIHRoZW0gZ2VuZXJpY2FsbHkgYXMgYGdlb21fWFhYWCgpYC4gDQoNClRoZXJlIGFyZSBvdmVyIDQwIGdlb21zIGluIGdncGxvdDIgYW5kIG1hbnkgb3RoZXJzIGNyZWF0ZWQgYnkgZmFucy4gVmlldyB0aGVtIGF0IHRoZSBgZ2dwbG90MmAgZ2FsbGVyeS4gU29tZSBjb21tb24gYGdlb21zYCBhcmUgbGlzdGVkIGJlbG93Og0KDQoqIEhpc3RvZ3JhbXMgLSBgZ2VvbV9oaXN0b2dyYW0oKWANCiogQmFyIGNoYXJ0cyAtIGBnZW9tX2JhcigpYCBvciBgZ2VvbV9jb2woKWAgDQoqIEJveCBwbG90cyAtIGBnZW9tX2JveHBsb3QoKWANCiogUG9pbnRzIChlLmcuIHNjYXR0ZXIgcGxvdHMpIC0gYGdlb21fcG9pbnQoKWANCiogTGluZSBncmFwaHMgLSBgZ2VvbV9saW5lKClgIG9yIGBnZW9tX3BhdGgoKWANCiogVHJlbmQgbGluZXMgLSBgZ2VvbV9zbW9vdGgoKWANCg0KV2UgY2FuIGRpc3BsYXkgb25lIG9yIG11bHRpcGxlIGBnZW9tc2AgaW4gb25lIHBsb3QuIEVhY2ggaXMgYWRkZWQgdG8gcHJldmlvdXMgYGdncGxvdDJgIGNvbW1hbmRzIHdpdGggYSBgK2AsIGFuZCB0aGV5IGFyZSBwbG90dGVkIHNlcXVlbnRpYWxseSBzdWNoIHRoYXQgbGF0ZXIgYGdlb21zYCBhcmUgcGxvdHRlZCBvbiB0b3Agb2YgcHJldmlvdXMgb25lcy4NCg0KIyBNYXBwaW5nIERhdGEgdG8gUGxvdA0KDQpgZ2VvbWAgZnVuY3Rpb25zIHJlcXVpcmUgbWFwcGluZyAoYXNzaWduaW5nKSBjb2x1bW5zIGluIHRoZSBkYXRhIHRvIGNvbXBvbmVudHMgb2YgdGhlIHBsb3QgbGlrZSB0aGUgYXhlcywgc2hhcGUgY29sb3JzLCBzaGFwZSBzaXplcywgZXRjLiBUaGUgbWFwcGluZ3MgbXVzdCBiZSB3cmFwcGVkIGluIHRoZSBgYWVzKClgIGZ1bmN0aW9uLCBzbyB3ZSB3b3VsZCB3cml0ZSBzb21ldGhpbmcgbGlrZSBgbWFwcGluZyA9IGFlcyh4ID0gY29sMSwgeSA9IGNvbDIpYC4NCg0KRm9yIGV4YW1wbGUsIGluIHRoZSBmb2xsb3dpbmcgZXhhbXBsZSB1c2luZyBgaXJpcyBkYXRhYCwgU2VwYWwgTGVuZ3RoIGlzIG1hcHBlZCB0byB0aGUgeC1heGlzLCBhbmQgU2VwYWwgV2lkdGggaXMgbWFwcGVkIHRvIHRoZSB5LWF4aXMuIEFmdGVyIGEgKywgdGhlIHBsb3R0aW5nIGNvbW1hbmRzIGNvbnRpbnVlLiBBIHNoYXBlIGlzIGNyZWF0ZWQgd2l0aCB0aGUg4oCcZ2VvbeKAnSBmdW5jdGlvbiBnZW9tX3BvaW50KCkuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQpnZ3Bsb3QoZGF0YSA9IGlyaXMsIG1hcHBpbmcgPSBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoKSkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KDQpXaGVuIGNyZWF0aW5nIGEgaGlzdG9ncmFtLCBvbmx5IG9uZSB2YXJpYWJsZSBpcyB1c2VkLiBTZWUgdGhlIGZvbGxvd2luZyBleGFtcGxlLg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KZ2dwbG90KGRhdGEgPSBpcmlzLCBtYXBwaW5nID0gYWVzKHggPSBQZXRhbC5XaWR0aCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjIpDQpgYGANCg0KIyMgQXJyYW5naW5nIE11bHRpcGxlIEdyb2JzIG9uIHRoZSBTYW1lIFBhZ2UNCg0KSW4gdGhlIGFib3ZlIHN1YnNlY3Rpb24sIHdlIGNyZWF0ZSB0d28gZ3JhcGhzIG9uIHR3byBkaWZmZXJlbnQgcGFnZXMuIFNvbWV0aW1lcywgd2Ugd2FudCB0byBwbGFjZSB0d28gbW9yZSBncmFwaHMgb24gdGhlIHNhbWUgcGFnZSBmb3IgY29tcGFyaXNvbiBwdXJwb3Nlcy4gSW4gYmFzZSBSLCB3ZSBoYXZlIGdyYXBoaWMgZnVuY3Rpb25zIHN1Y2ggYXMgYHBhcigpYCBhbmQgYGxheW91dCgpYCB0byBzZXQgdXAgYSBsYXlvdXQgZm9yIHRoZSBncmFwaGljIHBhZ2UuDQoNCkluIHRoaXMgbm90ZSwgd2UgaW50cm9kdWNlIHRoZSBsaWJyYXJ5IGBjb3dwbG90YCB0byBhcnJhbmdlIG11bHRpcGxlIGdyYXBoaWNhbCBvYmplY3RzIChhLmsuYSBgZ3JvYnNgKSBvbiBhIHBhZ2UuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQojIyBOYW1lIHRoZSB0d28gcGxvdHMgZmlyc3QgYW5kIHRoZW4gY2FsbCB0aGUgdHdvIGdyb2JzIGluIHRoZSBsYXlvdXQgZnVuY3Rpb24NCiMjIHNjYXR0ZXIgcGxvdA0Kc2NhdHRlciA9IGdncGxvdChkYXRhID0gaXJpcywgbWFwcGluZyA9IGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgpKSArDQogIGdlb21fcG9pbnQoKQ0KIyMgaGlzdG9ncmFtDQpoaXN0ID0gZ2dwbG90KGRhdGEgPSBpcmlzLCBtYXBwaW5nID0gYWVzKHggPSBQZXRhbC5XaWR0aCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjIpDQojIyB1c2UgcGxvdF9ncmlkKCkgaW4ge2Nvd3Bsb3R9IHRvIGxheW91dCB0aGUgdHdvIHBsb3RzDQpwbG90X2dyaWQoc2NhdHRlciwgaGlzdCwgbGFiZWxzPWMoIkEiLCAiQiIpLCBuY29sID0gMiwgbnJvdyA9IDEpDQpgYGANCg0KIyMgUGxvdCBBZXN0aGV0aWNzDQoNCkluIGBnZ3Bsb3RgIHRlcm1pbm9sb2d5IGEgcGxvdCDigJxhZXN0aGV0aWPigJ0gaGFzIGEgc3BlY2lmaWMgbWVhbmluZy4gSXQgcmVmZXJzIHRvIGNvbG9ycywgc2l6ZXMsIHRyYW5zcGFyZW5jaWVzLCBwbGFjZW1lbnQsIGV0Yy4gb2YgdGhlIHBsb3R0ZWQgZGF0YS4gYE5vdCBhbGwgZ2VvbXMgd2lsbCBoYXZlIHRoZSBzYW1lIGFlc3RoZXRpYyBvcHRpb25zYCwgYnV0IG1hbnkgY2FuIGJlIHVzZWQgYnkgbW9zdCBgZ2VvbXNgLiANCg0KSGVyZSBhcmUgc29tZSBleGFtcGxlczoNCg0KKiBgc2hhcGVgID0gRGlzcGxheSBhIHBvaW50IHdpdGggYGdlb21fcG9pbnQoKWAgYXMgYSBgZG90YCwgYHN0YXJgLCBgdHJpYW5nbGVgLCBvciBgc3F1YXJlYCwgZXRjLg0KKiBgZmlsbGAgPSBUaGUgaW50ZXJpb3IgY29sb3IgKGUuZy4gb2YgYSBiYXIgb3IgYm94cGxvdCkNCiogYGNvbG9yYCA9IFRoZSBleHRlcmlvciBsaW5lIG9mIGEgYmFyLCBib3hwbG90LCBldGMuLCBvciB0aGUgcG9pbnQgY29sb3IgaWYgdXNpbmcgYGdlb21fcG9pbnQoKWANCiogYHNpemVgID0gU2l6ZSAoZS5nLiBsaW5lIHRoaWNrbmVzcywgcG9pbnQgc2l6ZSkNCiogYGFscGhhYCA9IFRyYW5zcGFyZW5jeSAoMSA9IG9wYXF1ZSwgMCA9IGludmlzaWJsZSkNCiogYGJpbndpZHRoYCA9IFdpZHRoIG9mIGhpc3RvZ3JhbSBiaW5zDQoqIGB3aWR0aGAgPSBXaWR0aCBvZiDigJxiYXIgcGxvdOKAnSBjb2x1bW5zDQoqIGBsaW5ldHlwZWAgPSBMaW5lIHR5cGUgKGUuZy4gYHNvbGlkYCwgYGRhc2hlZGAsIGBkb3R0ZWRgKQ0KDQpUaGUgYWVzdGhldGljcyBvZiBwbG90IG9iamVjdHMgY2FuIGJlIGFzc2lnbmVkIHZhbHVlcyBpbiB0d28gd2F5czoNCg0KMS4gQXNzaWduZWQgYSBzdGF0aWMgdmFsdWUgKGUuZy4gY29sb3IgPSAiYmx1ZSIpIHRvIGFwcGx5IGFjcm9zcyBhbGwgcGxvdHRlZCBvYnNlcnZhdGlvbnMNCg0KMi4gQXNzaWduZWQgdG8gYSBjb2x1bW4gb2YgdGhlIGRhdGEgKGUuZy4gY29sb3IgPSBob3NwaXRhbCkgc3VjaCB0aGF0IHRoZSBkaXNwbGF5IG9mIGVhY2ggb2JzZXJ2YXRpb24gZGVwZW5kcyBvbiBpdHMgdmFsdWUgaW4gdGhhdCBjb2x1bW4NCg0KV2UgaGF2ZSBhbHJlYWR5IGFkZGVkIGJpbndpZHRoIHRvIHRoZSBhYm92ZSBoaXN0b2dyYW0uIE5leHQsIHdlIGFkZCBjb2xvciB0byB0aGUgaGlzdG9ncmFtDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTR9DQojIENoYW5nZSBoaXN0b2dyYW0gcGxvdCBsaW5lIGNvbG9ycyBieSBncm91cHMNCnNjYXR0ZXIwMSA8LSBnZ3Bsb3QoaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTZXBhbC5XaWR0aCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFNwZWNpZXMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IFBldGFsLldpZHRoKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC41KQ0KIyBPdmVybGFpZCBoaXN0b2dyYW1zDQpoaXN0MDEgPC0gZ2dwbG90KGlyaXMsIGFlcyh4ID0gUGV0YWwuV2lkdGgsIGNvbG9yPVNwZWNpZXMpKSArDQogICAgICAgICAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0ibmF2eSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC43LCANCiAgICAgICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9ICJpZGVudGl0eSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGJpbndpZHRoID0gMC4yKQ0KIyMgdXNlIHBsb3RfZ3JpZCgpIGluIHtjb3dwbG90fSB0byBsYXkgb3V0IHRoZSB0d28gcGxvdHMNCnBsb3RfZ3JpZChzY2F0dGVyMDEsIGhpc3QwMSwgbGFiZWxzPWMoIkEiLCAiQiIpLCBuY29sID0gMiwgbnJvdyA9IDEpDQpgYGANCg0KIyMgTGFiZWxzIGluIGBnZ3Bsb3QoKWANCg0KU3VyZWx5IHlvdSB3aWxsIHdhbnQgdG8gYWRkIG9yIGFkanVzdCB0aGUgcGxvdOKAmXMgbGFiZWxzLiBUaGVzZSBhcmUgbW9zdCBlYXNpbHkgZG9uZSB3aXRoaW4gdGhlIGBsYWJzKClgIGZ1bmN0aW9uIHdoaWNoIGlzIGFkZGVkIHRvIHRoZSBwbG90IHdpdGggYCtgIGp1c3QgYXMgdGhlIGBnZW9tc2Agd2VyZS4NCg0KV2l0aGluIGBsYWJzKClgIHlvdSBjYW4gcHJvdmlkZSBjaGFyYWN0ZXIgc3RyaW5ncyB0byB0aGVzZSBhcmd1bWVudHM6DQoNCiogYHggPWAgYW5kIGB5ID1gOiAgVGhlIHgtYXhpcyBhbmQgeS1heGlzIHRpdGxlIChsYWJlbHMpDQoNCiogYHRpdGxlID1gOiBUaGUgbWFpbiBwbG90IHRpdGxlDQoNCiogYHN1YnRpdGxlID1gOiBUaGUgc3VidGl0bGUgb2YgdGhlIHBsb3QsIGluIHNtYWxsZXIgdGV4dCBiZWxvdyB0aGUgdGl0bGUNCg0KKiBgY2FwdGlvbiA9YDogVGhlIGNhcHRpb24gb2YgdGhlIHBsb3QsIGluIGJvdHRvbS1yaWdodCBieSBkZWZhdWx0DQoNCkhlcmUgaXMgYSBwbG90IHdlIG1hZGUgZWFybGllciwgYnV0IHdpdGggbmljZXIgbGFiZWxzOg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KZ2dwbG90KGlyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBTcGVjaWVzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSBQZXRhbC5XaWR0aCkpICsNCiAgICAgICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArDQogICAgICAgICAgICAgbGFicygNCiAgICAgICAgICAgICAgICAgeCA9ICJTZXBhbCBMZW5ndGgiLA0KICAgICAgICAgICAgICAgICB5ID0gIlNlcGFsIFdpZHRoIiwNCiAgICAgICAgICAgICAgICAgIyBsYWJlbCBmb3IgbGVnZW5kcw0KICAgICAgICAgICAgICAgICBzaXplID0gIlNlcGFsIExlbmd0aDoiLA0KICAgICAgICAgICAgICAgICBjb2xvciA9ICJTcGVjaWVzOiIsDQogICAgICAgICAgICAgICAgIHRpdGxlID0gIkFzc29jaWF0aW9uIGJldHdlZW4gU2VwYWwgTGVuZ3RoIGFuZCBXaWR0aCIsDQogICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gIlRoaXMgaXMgYSBwYXJ0aWFsIHNjYXR0ZXIgcGxvdCIsDQogICAgICAgICAgICAgICAgIGNhcHRpb24gPSBwYXN0ZSgiQ3JlYXRlZCBvbiIsIFN5cy5EYXRlKCkpKSArDQogICAgICAgICAgICAgdGhlbWVfbWluaW1hbCgpICAgIyBtaW5pbWFsIHRoZW1lDQpgYGANCg0KDQojIyAgVGhlbWVzIGluIGBnZ3Bsb3QoKWANCg0KVGhlIHRoZW1lIHN5c3RlbSBpbiBgZ2dwbG90KClgIGRvZXMgbm90IGFmZmVjdCBob3cgdGhlIGRhdGEgaXMgcmVuZGVyZWQgYnkgYGdlb21zYCwgb3IgaG93IGl0IGlzIHRyYW5zZm9ybWVkIGJ5IHNjYWxlcy4gYFRoZW1lc2AgZG9u4oCZdCBjaGFuZ2UgdGhlIHBlcmNlcHR1YWwgcHJvcGVydGllcyBvZiB0aGUgcGxvdCwgYnV0IHRoZXkgZG8gaGVscCB5b3UgbWFrZSB0aGUgcGxvdCBhZXN0aGV0aWNhbGx5IHBsZWFzaW5nIG9yIG1hdGNoIGFuIGV4aXN0aW5nIHN0eWxlIGd1aWRlLiBgVGhlbWVzYCBnaXZlIHVzIGNvbnRyb2wgb3ZlciB0aGluZ3MgbGlrZSBmb250cywgdGlja3MsIHBhbmVsIHN0cmlwZXMsIGFuZCBiYWNrZ3JvdW5kcy4NCg0KSW4gb3RoZXIgd29yZHMsIHdoZW4gY3JlYXRpbmcgdGhlIHBsb3Qgd2UgZGV0ZXJtaW5lIGhvdyB0aGUgZGF0YSBpcyBkaXNwbGF5ZWQsIGFuZCB0aGVuIGFmdGVyIGl0IGhhcyBiZWVuIGNyZWF0ZWQgd2UgY2FuIGVkaXQgZXZlcnkgZGV0YWlsIG9mIHRoZSByZW5kZXJpbmcsIHVzaW5nIHRoZSB0aGVtaW5nIHN5c3RlbS4NCg0KVGhlIHRoZW1pbmcgc3lzdGVtIGlzIGNvbXBvc2VkIG9mIGZvdXIgbWFpbiBjb21wb25lbnRzOg0KDQoqIFRoZW1lIGVsZW1lbnRzIHNwZWNpZnkgdGhlIG5vbi1kYXRhIGVsZW1lbnRzIHRoYXQgd2UgY2FuIGNvbnRyb2wuIEZvciBleGFtcGxlLCANCiAgKyBgcGxvdC50aXRsZWAgIGNvbnRyb2xzIHRoZSBhcHBlYXJhbmNlIG9mIHRoZSBwbG90IHRpdGxlOw0KICArIGBheGlzLnRpY2tzLnhgIGNvbnRyb2xzIHRoZSB0aWNrcyBvbiB0aGUgeC1heGlzOyANCiAgKyBgbGVnZW5kLmtleS5oZWlnaHRgIGNvbnRyb2xzIHRoZSBoZWlnaHQgb2YgdGhlIGtleXMgaW4gdGhlIGxlZ2VuZC4NCg0KKiBFYWNoIGVsZW1lbnQgaXMgYXNzb2NpYXRlZCB3aXRoIGFuIGVsZW1lbnQgZnVuY3Rpb24sIHdoaWNoIGRlc2NyaWJlcyB0aGUgdmlzdWFsIHByb3BlcnRpZXMgb2YgdGhlIGVsZW1lbnQuIEZvciBleGFtcGxlLCBgZWxlbWVudF90ZXh0KClgIHNldHMgdGhlIGZvbnQgc2l6ZSwgY29sb3IsIGFuZCBmYWNlIG9mIHRleHQgZWxlbWVudHMgbGlrZSBgcGxvdC50aXRsZWAuDQoNCiogVGhlIGB0aGVtZSgpYCBmdW5jdGlvbiB3aGljaCBhbGxvd3MgeW91IHRvIG92ZXJyaWRlIHRoZSBkZWZhdWx0IHRoZW1lIGVsZW1lbnRzIGJ5IGNhbGxpbmcgZWxlbWVudCBmdW5jdGlvbnMsIGxpa2UgYHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gInJlZCIpKWAuDQoNCiogQ29tcGxldGUgdGhlbWVzLCBsaWtlIGB0aGVtZV9ncmV5KClgIHNldCBhbGwgb2YgdGhlIHRoZW1lIGVsZW1lbnRzIHRvIHZhbHVlcyBkZXNpZ25lZCB0byB3b3JrIHRvZ2V0aGVyIGhhcm1vbmlvdXNseS4NCg0KSGVyZSBhcmUgc29tZSBlc3BlY2lhbGx5IGNvbW1vbiB0aGVtZSgpIGFyZ3VtZW50cy4gWW91IHdpbGwgcmVjb2duaXplIHNvbWUgcGF0dGVybnMsIHN1Y2ggYXMgYXBwZW5kaW5nIC54IG9yIC55IHRvIGFwcGx5IHRoZSBjaGFuZ2Ugb25seSB0byBvbmUgYXhpcy4NCg0KPGJyPg0KPGNlbnRlcj48aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL3Blbmdkc2NpL3N0YTU1My9yYXcvbWFpbi9nZ3Bsb3QvdGhlbWUucG5nIiBhbHQ9InNhbXBsZSBnZ3Bsb3QgdGhlbWUiIGhlaWdodD0iNDAwIiB3aWR0aD0iNjUwIj48L2NlbnRlcj4NCjxicj4NCg0KVG8gZ2V0IHRoZSBjb21wbGV0ZSBsaXN0IG9mIHRoZW1lcywgcnVuIHRoZSBmb2xsb3dpbmcgY29kZQ0KDQpgYGB7cn0NCiN0aGVtZV9nZXQoKQ0KYGBgDQoNCg0KVG8gbWFrZSBzdXJlIHRoZSBwbG90IGNhbiBzdGFuZCBhbG9uZSwgd2UgbmVlZCB0byBwcm92aWRlIHRoZSBwbG90IHdpdGggYXhlcywgbGVnZW5kIGxhYmVscywgYW5kIHRpdGxlLCBhbmQgdHdlYWsgdGhlIGNvbG9yIHNjYWxlIGZvciBhcHByb3ByaWF0ZSBjb2xvcnMuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQojIGFkZGluZyB0aGVtZXMNCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gU3BlY2llcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gUGV0YWwuV2lkdGgpKSArDQogICAgICAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKw0KICAgICAgICAgICAgIGxhYnMoDQogICAgICAgICAgICAgICAgIHggPSAiU2VwYWwgTGVuZ3RoIiwNCiAgICAgICAgICAgICAgICAgeSA9ICJTZXBhbCBXaWR0aCIsDQogICAgICAgICAgICAgICAgICMgbGFiZWwgZm9yIGxlZ2VuZHMNCiAgICAgICAgICAgICAgICAgc2l6ZSA9ICJTZXBhbCBMZW5ndGg6IiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3BlY2llczoiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBc3NvY2lhdGlvbiBiZXR3ZWVuIFNlcGFsIExlbmd0aCBhbmQgV2lkdGgiICkgKw0KICAgICAgICAgICAgIHRoZW1lX21pbmltYWwoKSAgKyAgICMgbWluaW1hbCB0aGVtZQ0KICAgICAgICAgICAgIHRoZW1lKCAjIGxpc3Qgb2YgdGhlbWVzIGFwcGxpZWQgdG8gdGhlIHBsb3QNCiAgICAgICAgICAgICAgICAgICAjIHBsb3QgdGl0bGUgZmVhdHVyZXMNCiAgICAgICAgICAgICAgICAgICAjIGZvbnQgZmFtaWx5OiBjKCJzYW5zIiwgInNlcmlmIiwgIm1vbm8iKSAgDQogICAgICAgICAgICAgICAgICAgIyBmb250IGZhY2U6IGMoInBsYWluIiwgImJvbGQiLCAiaXRhbGljIiwgImJvbGQuaXRhbGljIikNCiAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gInNhbnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAwLjUpLCAjIGxlZnQoMCkscmlnaHQoMSkNCiAgICAgICAgICAgICAgICAgICAjIExhYmVscyBvZiBheGVzIA0KICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJyZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gIml0YWxpYyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJzZXJpZiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC41KSwNCiAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZqdXN0ID0gMC41KSwNCiAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG9yID0gInJlZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuNSksDQogICAgICAgICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImRhcmtibHVlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAic29saWQiKSwNCiAgICAgICAgICAgICAgICAgICAjIEF4aXMgdGljayBtYXJrcw0KICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2U9InBsYWluIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9InB1cnBsZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9MTEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuZ2xlPTQ1KSwNCiAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlPSJwbGFpbiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPSJvcmFuZ2UiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplPTExLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdsZT05MCksDQogICAgICAgICAgICAgICAgICAgIyBGZWF0dXJlcyBvZiBsZWdlbmQNCiAgICAgICAgICAgICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuMSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2dyZWVuIiksDQogICAgICAgICAgICAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDAuOSwgMC44KSwNCiAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICAgICAgICAgICAgICAgICAjIyBQYW5lbCBncmlkDQogICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJsaWdodGJsdWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjEpLA0KICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkNCiAgKQ0KYGBgDQoNCiMjIENvbXBsZXRlIENvbXBvbmVudHMgb2YgVGhlbWUNCg0KVGhlbWVzIGFyZSBhIHBvd2VyZnVsIHdheSB0byBjdXN0b21pemUgdGhlIGBub24tZGF0YSBjb21wb25lbnRzYCBvZiB0aGUgcGxvdHM6IGkuZS4gdGl0bGVzLCBsYWJlbHMsIGZvbnRzLCBiYWNrZ3JvdW5kLCBncmlkbGluZXMsIGFuZCBsZWdlbmRzLiBUbyBnaXZlIG91ciBwbG90cyBhIGNvbnNpc3RlbnQgY3VzdG9taXplZCBsb29rLCB3ZSBjYW4gZGVmaW5lIGEgdGhlbWUgZnVuY3Rpb24gYW5kIGNhbGwgdGhlIHRoZW1lIGZ1bmN0aW9uIGluIGFueSBgZ2dwbG90c2AuIA0KICANClRoZSBgdGlkeXZlcnNlYCBvZmZpY2lhbCB3ZWJzaXRlIHByb3ZpZGVzIGEgY29tcHJlaGVuc2l2ZSBkb2N1bWVudCBvbiB0aGVtZSBjb21wb25lbnRzIGluIGBnZ3Bsb3RgYC4gPGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS90aGVtZS5odG1sPi4gTnVtZXJvdXMgZXhhbXBsZXMgaGF2ZSBpbGx1c3RyYXRlZCBob3cgdG8gdXNlIHZhcmlvdXMgdGhlbWUgY29tcG9uZW50cy4NCg0KV2UgY2FuIGRlZmluZSBhIHRoZW1lIGZ1bmN0aW9uIHRoYXQgY2FuIGJlIHJldXNlZCB0byBjdXN0b21pemUgdGhlIHBsb3RzLiBGb3IgZXhhbXBsZSwgd2UgZGVmaW5lIHRoZSBmb2xsb3dpbmcgdGhlbWUgYW5kIHVzZSBpdCBpbiBkaWZmZXJlbnQgcGxvdHMuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQpteXBsb3QudGhlbWUgPC0gZnVuY3Rpb24oKSB7DQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSAic2FucyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya3JlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSksICMgbGVmdCgwKSxyaWdodCgxKQ0KICAgICMgYWRkIGJvcmRlciAxKQ0KICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmx1ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gTkEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZSA9IDIpLA0KICAgICMgY29sb3IgYmFja2dyb3VuZCAyKQ0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJhbGljZWJsdWUiKSwNCiAgICAjIG1vZGlmeSBncmlkIDMpDQogICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJzdGVlbGJsdWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuNSksDQogICAgcGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IueSA9ICBlbGVtZW50X2xpbmUoY29sb3VyID0gInN0ZWVsYmx1ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSAzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjUpLA0KICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAjIG1vZGlmeSB0ZXh0LCBheGlzIGFuZCBjb2xvdXIgNCkgYW5kIDUpDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJzdGVlbGJsdWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJpdGFsaWMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gIlRpbWVzIE5ldyBSb21hbiIpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gInN0ZWVsYmx1ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gIlRpbWVzIE5ldyBSb21hbiIpLA0KICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gInN0ZWVsYmx1ZSIpLA0KICAgICMgbGVnZW5kIGF0IHRoZSBib3R0b20gNikNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNiwgJ2NtJyksICNjaGFuZ2UgbGVnZW5kIGtleSBzaXplDQogICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuNiwgJ2NtJyksICNjaGFuZ2UgbGVnZW5kIGtleSBoZWlnaHQNCiAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgwLjYsICdjbScpLCAjY2hhbmdlIGxlZ2VuZCBrZXkgd2lkdGgNCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwgI2NoYW5nZSBsZWdlbmQgdGl0bGUgZm9udCBzaXplDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT04KSkgI2NoYW5nZSBsZWdlbmQgdGV4dCBmb250IHNpemUNCn0NCmBgYA0KDQpOb3cgd2UgdXNlIHRoZSBhYm92ZSB0aGVtZSBpbiB0aGUgZm9sbG93aW5nIHNjYXR0ZXIgcGxvdHMuIEluc3RlYWQgb2YgdXNpbmcgdGhlIGNvbG9ycyBiYXNlZCBvbiB0aGUgdmFsdWUgb2Ygc3BlY2llcywgd2UgbWFudWFsbHkgc2VsZWN0IGNvbG9ycyB0byBlbmNvZGUgdGhlIHZhbHVlcyBvZiBzcGVjaWVzLiBUaGUgZm9sbG93aW5nIFVSTCBsaW5rcyB0byBhIFBERiBkb2N1bWVudCB3aXRoIGNvbG9ycyBpbiBSLiA8aHR0cDovL3d3dy5zdGF0LmNvbHVtYmlhLmVkdS9+dHpoZW5nL2ZpbGVzL1Jjb2xvci5wZGY+DQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQojIENoYW5nZSBoaXN0b2dyYW0gcGxvdCBsaW5lIGNvbG9ycyBieSBncm91cHMNCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gZmFjdG9yKFNwZWNpZXMpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSBQZXRhbC5XaWR0aCkpICsNCiAgICAgICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArDQogICAgICAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJkb2RnZXJibHVlNCIsICJkYXJrb2xpdmVncmVlbjQiLCJkYXJrb3JjaGlkMyIpKSArDQogICAgICAgICAgICAgbGFicygNCiAgICAgICAgICAgICAgICAgeCA9ICJTZXBhbCBMZW5ndGgiLA0KICAgICAgICAgICAgICAgICB5ID0gIlNlcGFsIFdpZHRoIiwNCiAgICAgICAgICAgICAgICAgIyMgQ29sb3IgYW5kIHNpemUgb2YgbGFiZWxzDQogICAgICAgICAgICAgICAgIHNpemUgPSAiU2VwYWwgTGVuZ3RoOiIsDQogICAgICAgICAgICAgICAgIGNvbG9yID0gIlNwZWNpZXM6IiwNCiAgICAgICAgICAgICAgICAgdGl0bGUgPSAiQXNzb2NpYXRpb24gYmV0d2VlbiBTZXBhbCBMZW5ndGggYW5kIFdpZHRoIikgKw0KICAgICAgICAgICAgIG15cGxvdC50aGVtZSgpDQpgYGANCg0KTmV4dCwgd2UgcGxvdCBhIGhpc3RvZ3JhbSB1c2luZyB0aGUgc2FtZSB0aGVtZS4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChpcmlzLCBhZXMoeCA9IFBldGFsLldpZHRoLCBjb2xvcj1TcGVjaWVzKSkgKw0KICAgICAgICAgIGdlb21faGlzdG9ncmFtKGZpbGw9Im5hdnkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMywgDQogICAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSAiaWRlbnRpdHkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IDAuMikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImRvZGdlcmJsdWU0IiwgImRhcmtvbGl2ZWdyZWVuNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZGFya29yY2hpZDMiKSkgKw0KICAgICAgICAgICAgICAgbGFicygNCiAgICAgICAgICAgICAgICAgeCA9ICJQZXRhbCBXaWR0aCIsDQogICAgICAgICAgICAgICAgIGNvbG9yID0gIlNwZWNpZXM6IiwNCiAgICAgICAgICAgICAgICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFBldGFsIFdpZHRoIikgKw0KICAgICAgICAgICBteXBsb3QudGhlbWUoKQ0KYGBgDQoNCg0KDQoNCiMgQWRkaW5nIEFubm90YXRpb25zIHRvIEdyYXBoaWNzDQogDQpUbyBtYWtlIHRoZSBncmFwaGljIG1vcmUgaW5mb3JtYXRpdmUsIHNvbWV0aW1lcyB3ZSBtYXkgd2FudCB0byBhZGQgYW5ub3RhdGlvbnMgdG8gdGhlIGdyYXBoaWMuIElmIHdlIGNyZWF0ZSBhIHN0YXRpc3RpY2FsIGFuZCBwcm9iYWJpbGlzdGljIGdyYXBoaWMsIG9jY2FzaW9uYWxseSB3ZSBuZWVkIHRvIGFkZCBtYXRoZW1hdGljYWwgZXF1YXRpb25zIHdpdGggR3JlZWsgbGV0dGVycyB0byB0aGUgZ3JhcGhpY3MuIA0KDQojIyBBZGRpbmcgVGV4dCBBbm5vdGF0aW9uIHRvIEdyYXBoaWNzDQoNCiMjIyBBZGRpbmcgUGxhaW4gVGV4dCB0byBHcmFwaGljcw0KDQpUbyBhZGQgcGxhaW4gdGV4dCB0byBncmFwaGljcyBpbiBnZ3Bsb3QsIHdlIHVzZSB0aGUgZnVuY3Rpb24gYGFubm90YXRlKClgIHdpdGggZ2l2ZW4gY29vcmRpbmF0ZXMuIEZvciBleGFtcGxlLCB0aGUgc2NhdHRlciBwbG90IHNob3dzIHR3byBzZXBhcmF0ZSBncm91cHMuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQojIENoYW5nZSBoaXN0b2dyYW0gcGxvdCBsaW5lIGNvbG9ycyBieSBncm91cHMNCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gZmFjdG9yKFNwZWNpZXMpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSBQZXRhbC5XaWR0aCkpICsNCiAgICAgICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArDQogICAgICAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJkb2RnZXJibHVlNCIsICJkYXJrb2xpdmVncmVlbjQiLCAiZGFya29yY2hpZDMiKSkgKw0KICAgICAgICAgICAgIGxhYnMoDQogICAgICAgICAgICAgICAgIHggPSAiU2VwYWwgTGVuZ3RoIiwNCiAgICAgICAgICAgICAgICAgeSA9ICJTZXBhbCBXaWR0aCIsDQogICAgICAgICAgICAgICAgICMjIGNvbG9yIGFuZCBzaXplIG9mIGxhYmVscw0KICAgICAgICAgICAgICAgICBzaXplID0gIlNlcGFsIExlbmd0aDoiLA0KICAgICAgICAgICAgICAgICBjb2xvciA9ICJTcGVjaWVzOiIsDQogICAgICAgICAgICAgICAgIHRpdGxlID0gIkFzc29jaWF0aW9uIGJldHdlZW4gU2VwYWwgTGVuZ3RoIGFuZCBXaWR0aCIpICsNCiAgICAgICAgICAgICBteXBsb3QudGhlbWUoKSArIA0KICAgICAgICAgICAgIGFubm90YXRlKGdlb209InRleHQiLCANCiAgICAgICAgICAgICAgICAgICAgICB4PTcsIA0KICAgICAgICAgICAgICAgICAgICAgIHk9NC4xLCANCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1wYXN0ZSgiVGhlIGRpc3RyaWJ1dGlvbiBvZiBTZXRvc2EgaXMgZGlmZmVyZW50IiwgDQogICAgICAgICAgICAgICAgICAgICAgImZyb20gdGhhdCBvZiBWZXJzaWNvbG9yIGFuZCBWaWdpbmljYSIsIHNlcCA9ICJcbiIpLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPSJyZWQiLA0KICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC41KQ0KYGBgDQoNClNldmVyYWwgb3RoZXIgYWx0ZXJuYXRpdmVzIHdlIGNhbiB1c2UgdG8gYWRkIHRleHQgdG8gZ3JhcGhpY3MgY3JlYXRlZCB1c2luZyBgZ2dwbG90YGAuDQoNCiMjIyBQYXNzaW5nIFBhcmFtZXRlcnMgaW4gQW5ub3RhdGlvbg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KIyBDaGFuZ2UgaGlzdG9ncmFtIHBsb3QgbGluZSBjb2xvcnMgYnkgZ3JvdXBzDQpnZ3Bsb3QoaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgsIHkgPSBTZXBhbC5XaWR0aCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGZhY3RvcihTcGVjaWVzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gUGV0YWwuV2lkdGgpKSArDQogICAgICAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKw0KICAgICAgICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZG9kZ2VyYmx1ZTQiLCAiZGFya29saXZlZ3JlZW40IiwgImRhcmtvcmNoaWQzIikpICsNCiAgICAgICAgICAgICBsYWJzKA0KICAgICAgICAgICAgICAgICB4ID0gIlNlcGFsIExlbmd0aCIsDQogICAgICAgICAgICAgICAgIHkgPSAiU2VwYWwgV2lkdGgiLA0KICAgICAgICAgICAgICAgICAjIyBDb2xvciBhbmQgc2l6ZSBvZiBsYWJlbHMNCiAgICAgICAgICAgICAgICAgc2l6ZSA9ICJTZXBhbCBMZW5ndGg6IiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3BlY2llczoiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBc3NvY2lhdGlvbiBiZXR3ZWVuIFNlcGFsIExlbmd0aCBhbmQgV2lkdGgiKSArDQogICAgICAgICAgICAgbXlwbG90LnRoZW1lKCkgKyANCiAgICAgICAgICAgICAgYW5ub3RhdGUoZ2VvbT0idGV4dCIgLCANCiAgICAgICAgICAgICAgICAgICAgICAgeD03LCANCiAgICAgICAgICAgICAgICAgICAgICAgeT00LjQsDQogICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXBhc3RlKCJUaGUgUGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCByID0gIiwgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChjb3IoaXJpcyRTZXBhbC5MZW5ndGgsIGlyaXMkU2VwYWwuV2lkdGgpLDMpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsdWUiKQ0KYGBgDQoNClRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBiZXR3ZWVuIHNlcGFsIHdpZHRoIGFuZCBzZXBhbCBsZW5ndGggaXMgY2FsY3VsYXRlZCBkaXJlY3RseSBmcm9tIHRoZSBkYXRhIGFuZCBwYXNzZWQgdG8gdGhlIGFubm90YXRpb24gaW4gdGhlIGdyYXBoaWMuIE5vdGUgdGhhdCB3ZSB1c2VkIGEgdmVyeSBoYW5keSBhbmQgaW1wb3J0YW50IGdyYXBoaWMgZnVuY3Rpb24gYHBhc3RlKClgIHdoZW4gYWRkaW5nIHRoZSBhbm5vdGF0aW9uLg0KDQoNCiMjIEFkZGluZyBNYXRoZW1hdGljYWwgRXF1YXRpb25zIHRvIEdyYXBoaWNzDQoNCk1hdGhlbWF0aWNhbCBleHByZXNzaW9ucyBtYWRlIHdpdGggdGhlIHRleHQgYGdlb21zYCB1c2luZyBgcGFyc2UgPSBUUlVFYCBpbiBgZ2dwbG90MmAgaGF2ZSBhIGZvcm1hdCBzaW1pbGFyIHRvIHRob3NlIG1hZGUgd2l0aCBgcGxvdG1hdGgoKWAgYW5kIGBleHByZXNzaW9uKClgIGluIGJhc2UgUiwgZXhjZXB0IHRoYXQgdGhleSBhcmUgc3RvcmVkIGFzIHN0cmluZ3MsIHJhdGhlciB0aGFuIGFzIGV4cHJlc3Npb24gb2JqZWN0cy4NCg0KVG8gbWl4IHJlZ3VsYXIgdGV4dCB3aXRoIGV4cHJlc3Npb25zLCB1c2Ugc2luZ2xlIHF1b3RlcyB3aXRoaW4gZG91YmxlIHF1b3RlcyAob3IgdmljZSB2ZXJzYSkgdG8gbWFyayB0aGUgcGxhaW4tdGV4dCBwYXJ0cy4gRWFjaCBibG9jayBvZiB0ZXh0IGVuY2xvc2VkIGJ5IHRoZSBpbm5lciBxdW90ZXMgaXMgdHJlYXRlZCBhcyBhIHZhcmlhYmxlIGluIGEgbWF0aGVtYXRpY2FsIGV4cHJlc3Npb24uIA0KDQpCZWFyIGluIG1pbmQgdGhhdCwgaW4gUuKAmXMgc3ludGF4IGZvciBtYXRoZW1hdGljYWwgZXhwcmVzc2lvbnMsIHdlICoqY2Fu4oCZdCoqIHNpbXBseSBwdXQgYSB2YXJpYWJsZSByaWdodCBuZXh0IHRvIGFub3RoZXIgd2l0aG91dCBzb21ldGhpbmcgZWxzZSBpbiBiZXR3ZWVuLiBUbyBkaXNwbGF5IHR3byB2YXJpYWJsZXMgbmV4dCB0byBlYWNoIG90aGVyLCBwdXQgYSBgKmAgb3BlcmF0b3IgYmV0d2VlbiB0aGVtLiB3aGVuIGAqYCBpcyBkaXNwbGF5ZWQgaW4gYSBncmFwaGljLCBpdCBpcyB0cmVhdGVkIGFzIGFuIGludmlzaWJsZSBtdWx0aXBsaWNhdGlvbiBzaWduIChmb3IgYSB2aXNpYmxlIG11bHRpcGxpY2F0aW9uIHNpZ24sIHVzZSBgJSolYCk6DQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQp4LmF4aXMgPC0gc2VxKDAsIDIwLCBsZW5ndGgub3V0ID0gMTAwKQ0KeS5heGlzIDwtICgxL3NxcnQoMipwaSkqMykqZXhwKC0oeC5heGlzLTEwKV4yLygyKjkpKQ0Kbm9ybWFsLmRhdGEgPSBkYXRhLmZyYW1lKHg9eC5heGlzICwgeT15LmF4aXMpDQojIw0KZ2dwbG90KG5vcm1hbC5kYXRhLCBhZXMoeCA9IHguYXhpcywgeSA9IHkuYXhpcykpICsgDQogICAgIGdlb21fbGluZShjb2xvciA9ICJibHVlIikgKw0KICAgICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwgMS4yNSksIHhsaW09YygwLDIwKSkgKyANCiAgICAgbGFicygNCiAgICAgICAgICAgICAgICAgeCA9ICJOb3JtYWwgU2NvcmUiLA0KICAgICAgICAgICAgICAgICB5ID0gIk5vcm1hbCBEZW5zaXR5IiwNCiAgICAgICAgICAgICAgICAgdGl0bGUgPSAiTm9ybWFsIERlbnNpdHkgQ3VydmUiKSArDQogICAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSAxMCwgeSA9IDAuMiwgDQogICAgICAgICAgICAgICBwYXJzZSA9IFRSVUUsIHNpemUgPSA0LA0KICAgICAgICAgICAgICBsYWJlbCA9ICInRnVuY3Rpb246ICAnICogeT09ZnJhYygxLCBzcXJ0KDIqcGkpKiBzaWdtYSkgJSolIGVeey0oeC0gbXUpXjIvMn0iLA0KICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiKQ0KYGBgDQoNCg0KDQojIyBBZGRpbmcgSW1hZ2VzIHRvIEV4aXN0aW5nIGdnUGxvdHMNCg0KVG8gZW1iZWQgYSBQTkcgaW1hZ2UgdG8gYW4gZXhpc3RpbmcgZ3JhcGggY3JlYXRlZCBieSBgZ2dwbG90YCwgd2UgbmVlZCB0byB1c2UgYHJlYWRQTkcoKWAgaW4gbGlicmFyeSAqKnBuZyoqIHRvIGxvYWQgdGhlIGltYWdlIHRvIFIgYW5kIGBnZXRVUkxjb250ZW50KClgIGluIHRoZSAqKlJDdXJsKiogIHRvIGluc2VydCB0aGUgaW1hZ2UgdG8gdGhlIGdyYXBoLg0KDQoNCg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KIyBjYXR1cmwgPC0gImh0dHBzOi8vc3RhdDU1My5zMy5hbWF6b25hd3MuY29tL2dncGxvdC9jYXQucG5nIg0KY2F0dXJsIDwtICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcGVuZ2RzY2kvc3RhNTUzL21haW4vZ2dwbG90L2NhdC5wbmciDQpteV9jYXQgPC0gIHJlYWRQTkcoZ2V0VVJMQ29udGVudChjYXR1cmwpKQ0KcmFzdGVyLmNhdCA8LSBhcy5yYXN0ZXIobXlfY2F0KSANCiMgQ2hhbmdlIGhpc3RvZ3JhbSBwbG90IGxpbmUgY29sb3JzIGJ5IGdyb3Vwcw0KZ2dwbG90KGlyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBmYWN0b3IoU3BlY2llcyksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IFBldGFsLldpZHRoKSkgKw0KICAgICAgICAgICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsNCiAgICAgICAgICAgICBsYWJzKA0KICAgICAgICAgICAgICAgICB4ID0gIlNlcGFsIExlbmd0aCIsDQogICAgICAgICAgICAgICAgIHkgPSAiU2VwYWwgV2lkdGgiLA0KICAgICAgICAgICAgICAgICAjIyBDb2xvciBhbmQgc2l6ZSBvZiBsYWJlbHMNCiAgICAgICAgICAgICAgICAgc2l6ZSA9ICJTZXBhbCBMZW5ndGg6IiwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3BlY2llczoiLA0KICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBc3NvY2lhdGlvbiBiZXR3ZWVuIFNlcGFsIExlbmd0aCBhbmQgV2lkdGgiKSArDQogICAgICAgICAgICAgbXlwbG90LnRoZW1lKCkgKyANCiAgICAgICAgICAgICAgYW5ub3RhdGlvbl9yYXN0ZXIocmFzdGVyLmNhdCwgNCwgNC44NSwgMy42NSwgNC41KQ0KYGBgDQoNCg0KDQojIyBSZW1vdmluZyBDaGFydCBKdW5rcw0KDQpXZSByZW1vdmUgc29tZSB1bm5lY2Vzc2FyeSBtYXJrcyBhbmQgY2hhbm5lbHMgZnJvbSB0aGUgY2hhcnQgdmlhIHRoZW1lOiBjaGFuZ2UgdGhlIGJhY2tncm91bmQgY29sb3IsIGdyaWQsIGFuZCBwbG90IHRpdGxlLg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KbXlwbG90LnRoZW1lX25ldyA8LSBmdW5jdGlvbigpIHsNCiAgdGhlbWUoDQogICAgI2dncGxvdCBtYXJnaW5zDQogICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKHQgPSA1MCwgICMgVG9wIG1hcmdpbg0KICAgICAgICAgICAgICAgICAgICAgICAgICByID0gMzAsICAjIFJpZ2h0IG1hcmdpbg0KICAgICAgICAgICAgICAgICAgICAgICAgICBiID0gMzAsICAjIEJvdHRvbSBtYXJnaW4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgbCA9IDMwKSwgIyBMZWZ0IG1hcmdpbg0KICAgICMjIGdncGxvdCB0aXRsZXMNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gInNhbnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIm5hdnkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAwLjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW49bWFyZ2luKDAsMCwzMCwwKSksICMgbGVmdCgwKSxyaWdodCgxKQ0KICAgICMgYWRkIGJvcmRlciAxKQ0KICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSBOQSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBOQSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gMiksDQogICAgIyBjb2xvciBiYWNrZ3JvdW5kIDIpDQogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiNmNmY2ZjYiKSwNCiAgICAjIG1vZGlmeSBncmlkIDMpDQogICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICd3aGl0ZScsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZSA9IDMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMC41KSwNCiAgICBwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gIGVsZW1lbnRfbGluZShjb2xvdXIgPSAnd2hpdGUnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gMywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMC41KSwNCiAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgIyBtb2RpZnkgdGV4dCwgYXhpcyBhbmQgY29sb3VyIDQpIGFuZCA1KQ0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAibmF2eSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjZmFjZSA9ICJpdGFsaWMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICNmYW1pbHkgPSAiVGltZXMgTmV3IFJvbWFuIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gIm5hdnkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSA3LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI2ZhbWlseSA9ICJUaW1lcyBOZXcgUm9tYW4iDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLA0KICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gIm5hdnkiKSwNCiAgICAjIGxlZ2VuZCBhdCB0aGUgYm90dG9tIDYpDQogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjYsICdjbScpLCAjY2hhbmdlIGxlZ2VuZCBrZXkgc2l6ZQ0KICAgIGxlZ2VuZC5rZXkuaGVpZ2h0ID0gdW5pdCgwLjYsICdjbScpLCAjY2hhbmdlIGxlZ2VuZCBrZXkgaGVpZ2h0DQogICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMC42LCAnY20nKSwgI2NoYW5nZSBsZWdlbmQga2V5IHdpZHRoDQogICAgI2xlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLCAjY2hhbmdlIGxlZ2VuZCB0aXRsZSBmb250IHNpemUNCiAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLCAgIyByZW1vdmUgYWxsIGxlZ2VuZCB0aXRsZXMNCiAgICBsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSwNCiAgICAjIyMjIw0KICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OCkpICNjaGFuZ2UgbGVnZW5kIHRleHQgZm9udCBzaXplDQp9DQpgYGANCg0KDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTV9DQojIENoYW5nZSBoaXN0b2dyYW0gcGxvdCBsaW5lIGNvbG9ycyBieSBncm91cHMNCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBmYWN0b3IoU3BlY2llcykpLCBsaW5ldHlwZSA9IFNwZWNpZXMpICsNCiAgICAgICAgICAgICBnZW9tX3BvaW50KHNpemUgPSAyLCBhbHBoYSA9IDAuNykgKw0KICAgICAgICAgICAgIHN0YXRfc21vb3RoKG1ldGhvZCA9IGxtLCBzZT1GQUxTRSwgc2l6ZSA9IDAuMykgKw0KICAgICAgICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiZG9kZ2VyYmx1ZTQiLCAiZGFya29saXZlZ3JlZW40IiwgImRhcmtvcmNoaWQzIikpICsNCiAgICAgICAgICAgICBsYWJzKA0KICAgICAgICAgICAgICAgICB4ID0gIlNlcGFsIExlbmd0aCIsDQogICAgICAgICAgICAgICAgIHkgPSAiU2VwYWwgV2lkdGgiLA0KICAgICAgICAgICAgICAgICAjIyBsYWJlbHMgb2YgY29sb3IgYW5kIHNpemUNCiAgICAgICAgICAgICAgICAgI3NpemUgPSAiU2VwYWwgTGVuZ3RoIiwNCiAgICAgICAgICAgICAgICAgI2NvbG9yID0gTkEsDQogICAgICAgICAgICAgICAgIHRpdGxlID0gIkFzc29jaWF0aW9uIGJldHdlZW4gU2VwYWwgTGVuZ3RoIGFuZCBXaWR0aCIpICsNCiAgICAgICAgICAgICBteXBsb3QudGhlbWVfbmV3KCkgKyANCiAgICAgICAgICAgICAgYW5ub3RhdGUoZ2VvbT0idGV4dCIgLCANCiAgICAgICAgICAgICAgICAgICAgICAgeD02LjgsIA0KICAgICAgICAgICAgICAgICAgICAgICB5PTIsDQogICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXBhc3RlKCJUaGUgUGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCByID0gIiwgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChjb3IoaXJpcyRTZXBhbC5MZW5ndGgsIGlyaXMkU2VwYWwuV2lkdGgpLDMpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJuYXZ5IikgKyANCiAgICAgICAgICAgICAgIGNvb3JkX2ZpeGVkKDEpICAgICMjIFRoaXMgY2hhbmdlcyB0aGUgYXNwZWN0IHJhdGlvIG9mIHRoZSBncmFwaA0KYGBgDQoNCg0KDQoNCg0KIyBBbWluYXRlZCBHcmFwaCB3aXRoIGBnZ2FuaW1hdGUoKWANCg0KYGdnYW5pbWF0ZSgpYCBleHRlbmRzIHRoZSBncmFtbWFyIG9mIGdyYXBoaWNzIGFzIGltcGxlbWVudGVkIGJ5IGBnZ3Bsb3QyYCB0byBpbmNsdWRlIHRoZSBkZXNjcmlwdGlvbiBvZiBhbmltYXRpb24uIEl0IGRvZXMgdGhpcyBieSBwcm92aWRpbmcgYSByYW5nZSBvZiBuZXcgZ3JhbW1hciBjbGFzc2VzIHRoYXQgY2FuIGJlIGFkZGVkIHRvIHRoZSBwbG90IG9iamVjdCBpbiBvcmRlciB0byBjdXN0b21pemUgaG93IGl0IHNob3VsZCBjaGFuZ2Ugd2l0aCB0aW1lLg0KDQoqIGB0cmFuc2l0aW9uXyooKWAgZGVmaW5lcyBob3cgdGhlIGRhdGEgc2hvdWxkIGJlIHNwcmVhZCBvdXQgYW5kIGhvdyBpdCByZWxhdGVzIHRvIGl0c2VsZiBhY3Jvc3MgdGltZS4NCg0KKiBgdmlld18qKClgIGRlZmluZXMgaG93IHRoZSBwb3NpdGlvbmFsIHNjYWxlcyBzaG91bGQgY2hhbmdlIGFsb25nIHdpdGggdGhlIGFuaW1hdGlvbi4NCg0KKiBgc2hhZG93XyooKWAgZGVmaW5lcyBob3cgZGF0YSBmcm9tIG90aGVyIHBvaW50cyBpbiB0aW1lIHNob3VsZCBiZSBwcmVzZW50ZWQgaW4gdGhlIGdpdmVuIHBvaW50IGluIHRpbWUuDQoNCiogYGVudGVyXyooKS9leGl0XyooKWAgZGVmaW5lcyBob3cgbmV3IGRhdGEgc2hvdWxkIGFwcGVhciBhbmQgaG93IG9sZCBkYXRhIHNob3VsZCBkaXNhcHBlYXIgZHVyaW5nIHRoZSBjb3Vyc2Ugb2YgdGhlIGFuaW1hdGlvbi4NCg0KKiBgZWFzZV9hZXMoKWAgZGVmaW5lcyBob3cgZGlmZmVyZW50IGFlc3RoZXRpY3Mgc2hvdWxkIGJlIGVhc2VkIGR1cmluZyB0cmFuc2l0aW9ucy4NCg0KVGhlIGxvZ2ljIGJlaGluZCB0aGUgYGdnYW5pbWF0ZWAgaXMgdG8gY3JlYXRlIGEgc2VxdWVuY2Ugb2YgaW1hZ2VzIGFuZCB0aGVuIG1ha2UgYSBnaWYgaW1hZ2UuIFdlIG5lZWQgdG8gd3JpdGUgSFRNTCB0byBpbmNsdWRlIHRoaXMgZ2lmIGluIHRoZSBSTWFya2Rvd24gZG9jdW1lbnQuDQoNCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmxpYnJhcnkoZ2FwbWluZGVyKQ0KDQpwIDwtIGdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gZ2RwUGVyY2FwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9bGlmZUV4cCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gcG9wLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IGNvdW50cnkpKSArDQogICAgICAgIGdlb21fcG9pbnQoYWVzKHNpemUgPSBwb3AsIGlkcyA9IGNvdW50cnkgKSwNCiAgICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNykgKw0KICAgICAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2QoKSArICAgICAgIyBjb2xvciBwYWxsZXRzIA0KICAgICAgICBzY2FsZV9zaXplKHJhbmdlID0gYygyLCAxMikpICsNCiAgICAgICAgc2NhbGVfeF9sb2cxMCgpICsNCiAgICAgICAgbGFicyh4ID0gIkdEUCBwZXIgY2FwaXRhIiwgDQogICAgICAgICAgICAgeSA9ICJMaWZlIGV4cGVjdGFuY3kiKSArDQogICAgICAgICMjIGdnYW5pbWF0ZSBjb21tYW5kDQogICAgICAgdHJhbnNpdGlvbl90aW1lKHllYXIpDQojIyANCmFuaW1fc2F2ZSgiTGlmZUV4cC5naWYiLCBwKQ0KIyAgYW5pbWF0ZShwLCByZW5kZXJlciA9IGdpZnNraV9yZW5kZXJlcigpKSAgIyBUaGlzIGNvbW1hbmQgd2lsbCBwb3AgdXAgYSBuZXcgZ3JhcGhpYyB3aW5kb3cgc2hvd2luZyB0aGUgYW5pbWF0aW9uLg0KYGBgDQoNCg0KPGJyPg0KPGNlbnRlcj48aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL3Blbmdkc2NpL3N0YTU1My9yYXcvbWFpbi9nZ3Bsb3QvTGlmZUV4cC5naWYiIGFsdD0iTGlmZUV4cGVjdGFuY3kgQW5pbWF0aW9uIiBoZWlnaHQ9IjQ1MCIgd2lkdGg9IjQwMCI+PC9jZW50ZXI+DQo8YnI+DQoNCg0KU2luY2UgdGhlIGdpZiBpbWFnZSBpcyBtYWRlIG9mIGluZGl2aWR1YWwgc3RhdGljIGltYWdlcywgaXQgaXMgZGlmZmVyZW50IGZyb20gdGhlIGludGVyYWN0aXZlIHBsb3QgcHJlc2VudGVkIGluIHRoZSBwcmV2aW91cyBzZWN0aW9ucyB0aGF0IGhhdmUgdGhlIGNhcGFiaWxpdHkgb2Ygc2hvd2luZyBtb2RlIGluZm9ybWF0aW9uIG9mIHRoZSBkYXRhIHZpYSBob3ZlciBtZXNzYWdlLiANCg0KVGhlIG5leHQgZ2lmIGdyYXBoIGNvbnNpc3RzIG9mIDUgcGFuZWxzLCBlYWNoIHJlcHJlc2VudGluZyBhIGNvbnRpbmVudC4gVGhleSBhcmUgYWxzbyBmaWcgaW1hZ2VzLiBUaGVyZWZvcmUsIG5vIGhvdmVyIG1lc3NhZ2UgaXMgYXZhaWxhYmxlIGZvciB0aGVzZSBnaWYgZmlndXJlcy4NCg0KV2UgdXNlIHRoZSB7Z2lma2l9IHBhY2thZ2UgdG8gcmVuZGVyIHRoZSBpbWFnZXMgaW4gdGhlIGZvcm0gb2YgZ2lmIGFuZCB0aGVuIGluY2x1ZGUgdGhlIGdpZiBpbWFnZSBpbnRvIHRoZSBSTWFya2Rvd24gZG9jdW1lbnQgZGlyZWN0bHkuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQp3IDwtIGdncGxvdChnYXBtaW5kZXIsIGFlcyhnZHBQZXJjYXAsIGxpZmVFeHAsIA0KICAgICAgICAgICAgICAgICAgc2l6ZSA9IHBvcCwgY29sb3VyID0gY291bnRyeSkpICsNCiAgICAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNywgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICAgICAgICAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGNvdW50cnlfY29sb3JzKSArDQogICAgICAgICAgICNzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImRvZGdlcmJsdWU0IiwgImRhcmtvbGl2ZWdyZWVuNCIsImRhcmtvcmNoaWQzIikpICsNCiAgICAgICAgICAgI3NjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQxIikgKw0KICAgICAgICAgICBzY2FsZV9zaXplKHJhbmdlID0gYygyLCAxMikpICsNCiAgICAgICAgICAgc2NhbGVfeF9sb2cxMCgpICsNCiAgICAgICAgICAgIyBicmVhayBkb3duIHRoZSBwcmV2aW91cyBzaW5nbGUgcGxvdCBieSBjb250aW5lbnQgDQogICAgICAgICAgICMgZmFjZXRfd3JhcCh+Y29udGluZW50KSArICAgICAjIGNyZWF0ZSBtdWx0aXBsZSBwYW5lbHMgYWNjb3JkaW5nIHRvIHRoZSBjb250aW5lbnRzDQogICAgICAgICAgICMgSGVyZSBjb21lcyB0aGUgZ2dhbmltYXRlIHNwZWNpZmljIGJpdHMNCiAgICAgICAgICAgbGFicyh0aXRsZSA9ICdZZWFyOiB7ZnJhbWVfdGltZX0nLCANCiAgICAgICAgICAgICAgICAgICAgeCA9ICdHRFAgcGVyIGNhcGl0YScsDQogICAgICAgICAgICAgICAgICAgIHkgPSAnbGlmZSBleHBlY3RhbmN5JykgKw0KICAgICAgICAgICB0cmFuc2l0aW9uX3RpbWUoeWVhcikgKw0KICAgICAgICAgICBlYXNlX2FlcygnbGluZWFyJykNCiMjIw0KYW5pbWF0ZSh3LCByZW5kZXJlciA9IGdpZnNraV9yZW5kZXJlcigpLA0KICAgICAgICAgIHJld2luZCA9IFRSVUUpDQpgYGANCg0KVGhlIGFib3ZlIGNvZGUgZG9lcyBub3Qgc2F2ZSB0aGUgZ2VuZXJhdGVkIGdpZiBpbWFnZSB0byB0aGUgZG9jdW1lbnQgZm9sZGVyIChkaXJlY3RvcnkpLiBJZiBuZWVkIHRvIHNhdmUgaXQgZnJvbSB0aGUgdmlld2VyIHdpbmRvdyB0byB0aGUgZGVzaWduYXRlZCBmb2xkZXIgYW5kIHRoZW4gZW1iZWQgaXQgdG8gYSB3ZWIgcGFnZSBjcmVhdGVkIGJ5IHRvb2xzIG90aGVyIHRoYW4gdGhlIFJNYXJrZG93bi4NCg0KYGBge30NCjxicj4NCjxjZW50ZXI+PGltZyBzcmM9Imh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9wZW5nZHNjaS9zdGE1NTMvbWFpbi9nZ3Bsb3QvTGlmZUV4cFJld2luZC5naWYiIGFsdD0iTGlmZSBFeHBlY3RhbmN5IEFuaW1hdGlvbiBSZXdpbmQiIGhlaWdodD0iNTAwIiB3aWR0aD0iNDAwIj48L2NlbnRlcj4NCjxicj4NCmBgYA0KDQpOZXh0LCB3ZSBjcmVhdGUgYSBncm91cCBnaWYgdXNpbmcgZmFjZXRfd3JhcCgpIGZ1bmN0aW9uLiBUaGUgY29kZSBpcyB0aGUgc2FtZSBhcyB0aGUgYWJvdmUgZXhhbXBsZSBleGNlcHQgZm9yIG9uZSBhZGRpdGlvbmFsIGZ1bmN0aW9uIGNhbGwuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQp3IDwtIGdncGxvdChnYXBtaW5kZXIsIGFlcyhnZHBQZXJjYXAsIGxpZmVFeHAsIA0KICAgICAgICAgICAgICAgICAgc2l6ZSA9IHBvcCwgY29sb3VyID0gY291bnRyeSkpICsNCiAgICAgICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNywgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICAgICAgICAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGNvdW50cnlfY29sb3JzKSArDQogICAgICAgICAgICNzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImRvZGdlcmJsdWU0IiwgImRhcmtvbGl2ZWdyZWVuNCIsImRhcmtvcmNoaWQzIikpICsNCiAgICAgICAgICAgI3NjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQxIikgKw0KICAgICAgICAgICBzY2FsZV9zaXplKHJhbmdlID0gYygyLCAxMikpICsNCiAgICAgICAgICAgc2NhbGVfeF9sb2cxMCgpICsNCiAgICAgICAgICAgIyBicmVhayBkb3duIHRoZSBwcmV2aW91cyBzaW5nbGUgcGxvdCBieSBjb250aW5lbnQgDQogICAgICAgICAgIGZhY2V0X3dyYXAofmNvbnRpbmVudCkgKyAgICAgIyBjcmVhdGUgbXVsdGlwbGUgcGFuZWxzIGFjY29yZGluZyB0byB0aGUgY29udGluZW50cw0KICAgICAgICAgICAjIEhlcmUgY29tZXMgdGhlIGdnYW5pbWF0ZS1zcGVjaWZpYyBiaXRzDQogICAgICAgICAgIGxhYnModGl0bGUgPSAnWWVhcjoge2ZyYW1lX3RpbWV9JywgDQogICAgICAgICAgICAgICAgICAgIHggPSAnR0RQIHBlciBjYXBpdGEnLA0KICAgICAgICAgICAgICAgICAgICB5ID0gJ2xpZmUgZXhwZWN0YW5jeScpICsNCiAgICAgICAgICAgdHJhbnNpdGlvbl90aW1lKHllYXIpICsNCiAgICAgICAgICAgZWFzZV9hZXMoJ2xpbmVhcicpDQojIyMNCmFuaW1hdGUodywgcmVuZGVyZXIgPSBnaWZza2lfcmVuZGVyZXIoKSwNCiAgICAgICAgICByZXdpbmQgPSBUUlVFKQ0KYGBgDQoNCg0KDQoNCg0KDQoNCiMgUmlkZ2V0bGluZSBQbG90IHdpdGggYGdncmlkZ2VzYCBMaWJyYXJ5DQoNClRoZSByaWRnZWxpbmUgcGxvdCBpcyBhIHVzZWZ1bCAzRCB0byBjb21wYXJlIG11bHRpcGxlIGRlbnNpdGllcy4gSXQgY3JlYXRlcyBhIDNEIGltcHJlc3Npb24gYW5kIGhhcyBnYWluZWQgaW5jcmVhc2luZyBwb3B1bGFyaXR5LiBIZXJlIHdlIHVzZSB0aGUgQ2FsaWZvcm5pYSBIb3VzaW5nIERhdGEgdGhhdCBpcyBhdmFpbGFibGUgb24gdGhlIFByb2plY3QgRGF0YSBTZXQgPGh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL2RhdGFzZXRzLyNjYWwtaG91c2luZz4uDQoNCmBgYHtyfQ0KQ2FsSG91c2luZyA9IHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcGVuZ2RzY2kvc3RhNTUzLmh0bWwvbWFpbi9kYXRhL2NhLWhvdXNpbmctcHJpY2UuY3N2IikNCmdncGxvdChDYWxIb3VzaW5nLCBhZXMoeCA9IG1lZGlhbl9ob3VzZV92YWx1ZSwgeSA9IG9jZWFuX3Byb3hpbWl0eSwgZmlsbCA9IG9jZWFuX3Byb3hpbWl0eSkpICsNCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcygpDQpgYGANCg0KDQpZb3UgY2FuIHBhc3MgYHN0YXQoeClgIG9yIGBmYWN0b3Ioc3RhdCh4KSlgIHRvIHRoZSBmaWxsIGFyZ3VtZW50IG9mIGBhZXNgIGFuZCB1c2UgYGdlb21fZGVuc2l0eV9yaWRnZXNfZ3JhZGllbnRgIGFuZCBhIGNvbnRpbnVvdXMgZmlsbCBjb2xvciBzY2FsZSB0byBmaWxsIGVhY2ggcmlkZ2VsaW5lIHdpdGggYSBncmFkaWVudC4NCg0KYGBge3J9DQpnZ3Bsb3QoQ2FsSG91c2luZywgYWVzKHggPSBtZWRpYW5faG91c2VfdmFsdWUsIHkgPSBvY2Vhbl9wcm94aW1pdHksIGZpbGwgPSBzdGF0KHgpKSkgKw0KICAgICAgZ2VvbV9kZW5zaXR5X3JpZGdlc19ncmFkaWVudChqaXR0ZXJlZF9wb2ludHMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9wb2ludHNfaml0dGVyKHdpZHRoID0gMC4wNSwgaGVpZ2h0ID0gMCksDQogICAgcG9pbnRfc2hhcGUgPSAnfCcsIHBvaW50X3NpemUgPSAxLCBwb2ludF9hbHBoYSA9IDEsIGFscGhhID0gMC4zLCkgKyANCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MobmFtZSA9ICJtZWRpYW5faG91c2VfdmFsdWUiLCBvcHRpb24gPSAiQyIpIA0KYGBgDQoNCk5leHQsIHdlIGV4cGxvcmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjb250aW51b3VzIHZhcmlhYmxlcyBpbiB0aGUgaXJpcyBkYXRhIHNldC4gQXMgYW4gZXhhbXBsZSwgd2UgbWFrZSB0aGUgZm9sbG93aW5nIHJpZGdlbGluZSBwbG90IHRvIHNlZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHNlcGFsIHdpZHRocyBhY3Jvc3MgdGhlIHNwZWNpZXMuDQoNCmBgYHtyfQ0KZ2dwbG90KGlyaXMsIGFlcyh4ID0gU2VwYWwuV2lkdGgsIHkgPSBTcGVjaWVzLCBmaWxsID0gc3RhdCh4KSkpICsNCiAgICAgIGdlb21fZGVuc2l0eV9yaWRnZXNfZ3JhZGllbnQoaml0dGVyZWRfcG9pbnRzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fcG9pbnRzX2ppdHRlcih3aWR0aCA9IDAuMDUsIGhlaWdodCA9IDApLA0KICAgIHBvaW50X3NoYXBlID0gJ3wnLCBwb2ludF9zaXplID0gMSwgcG9pbnRfYWxwaGEgPSAxLCBhbHBoYSA9IDAuMywpICsgDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG5hbWUgPSAiU2VwYWwgV2lkdGgiLCBvcHRpb24gPSAiQyIpIA0KYGBgDQoNCg0KVGhlIGFib3ZlIGRpc3RyaWJ1dGlvbnMgaGF2ZSBzaW1pbGFyIHNoYXBlcyAodmFyaWF0aW9ucykgYnV0IHdpdGggZGlmZmVyZW50IG1lYW5zLiBUaGlzIGFsc28gaW5kaWNhdGVzIHRoZSBBTk9WQSBtb2RlbCBiZXR3ZWVuIHNlcGFsIHdpZHRoIGFuZCBzcGVjaWVzIGlzIGFwcHJvcHJpYXRlLg0KDQoNCiMgT3RoZXIgRXh0ZW5zaW9ucyB0byBnZ3Bsb3QNCg0KV2UgaGF2ZSB1c2VkIGdncGxvdCBleHRlbnNpb25zICoqe2dnYW5pbWF0ZX0qKiB0byBjcmVhdGUgYW5pbWF0ZWQgZ3JhcGhzIGFuZCAqKntnZ3JpZGdlc30qKiB0byBjcmVhdGUgcmlkZ2VsaW5lIGdyYXBocyB0byBjb21wYXJlIG11bHRpcGxlIGRlbnNpdGllcy4gVGhlcmUgYXJlIHNldmVyYWwgb3RoZXIgaW1wb3J0YW50IGdncGxvdCBleHRlbnNpb25zIHRoYXQgZW5oYW5jZSB0aGUgYmFzaWMgZ2dwbG90cy4NCg0KDQoqIGdnZGVuZHJvIC0gY29udHJvbHMgdGhlIGFwcGVhcmFuY2UgYW5kIGRpc3BsYXkgb2YgeW91ciBjbHVzdGVyIGFuYWx5c2VzDQoNCiogZ2d0aGVtZXMgLSBjb250YWlucyB0aGVtZXMgYW5kIHNjYWxlcyB0aGF0IGVuaGFuY2UgdGhlIHN0YW5kYXJkIGdncGxvdHMuIA0KDQoqIGdncHViciAtIG1ha2VzIGl0IGVhc3kgdG8gcHJvZHVjZSBwdWJsaWNhdGlvbi1yZWFkeSBwbG90cyB1c2luZyBnZ3Bsb3QuIA0KDQoqIFBsb3RseSAtIGJyaW5ncyBpbnRlcmFjdGl2aXR5IHRvIGdncGxvdHMuIFdlIHdpbGwgc3BlbmQgYSB3ZWVrIG9uIHBsb3RseSgpLg0KDQoqIHBhdGNod29yayAtIGFycmFuZ2VzIG11bHRpcGxlIFIgcGxvdHMgb24gdGhlIHNhbWUgZ3JhcGhpY3MgcGFnZQ0KDQoqIGdnbWFwIC0gaXMgYSBwb3dlcmZ1bCBwYWNrYWdlIGZvciB2aXN1YWxpemluZyBzcGF0aWFsIGRhdGEgYW5kIG1vZGVscy4gSXQgbGF5ZXJzIGRhdGEgb24gdG9wIG9mIHN0YXRpYyBtYXBzIGZyb20gcG9wdWxhciBvbmxpbmUgc291cmNlcy4gV2Ugd2lsbCB1c2UgdGhlc2UgcGFja2FnZXMgdG8gbWFrZSBtYXBzIGxhdGVyLg0KDQoqIGdncmVwZWwgLSBnaXZlcyBnZ3Bsb3QyIHVzZXJzIGdyZWF0ZXIgY29udHJvbCBvdmVyIGhvdyB0ZXh0IGxhYmVscyBhcHBlYXIgaW4gdGhlaXIgY2hhcnRzLiANCg0KKiBnZ2NvcnJwbG90IC0gY29udHJvbHMgdGhlIGFwcGVhcmFuY2Ugb2YgdGhlIG1hdHJpeCwgZnJvbSBhbHRlcmluZyB0aGUgY29sb3IsIHNoYXBlLCBvciBzaXplIG9mIHRoZSBib3hlcyAoYXMgaW4gdGhlIGNpcmNsZS1tYXRyaXggYWJvdmUpLCB0byBhZGRpbmcgY29lZmZpY2llbnQgbGFiZWxzLCByZW9yZGVyaW5nIHRoZSBtYXRyaXggYWNjb3JkaW5nIHRvIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLCBhbmQgc28gb24uDQoNCiogR0dhbGx5IC0gYnJpbmdzIHRvZ2V0aGVyIG1hbnkgdXNlZnVsIGFkZGl0aW9uYWwgdmlzdWFsaXphdGlvbiBmdW5jdGlvbmFsaXR5LCBhbGwgaW4gb25lIHBhY2thZ2UuDQoNCiogZ2dpcmFwaCAtaXMgYW4gaHRtbHdpZGdldCB0aGF0IGNhbiBiZSBleHRlbmRlZCB0byBhbiBleGlzdGluZyBnZ3Bsb3QyIHN1Y2ggYXMgYmFyIGNoYXJ0LCBzY2F0dGVycGxvdCwgYm94cGxvdCwgbWFwLCBldGMuLCBhbmQgZG9lcyB0aGluZ3MgbGlrZSBkaXNwbGF5aW5nIGEgdG9vbHRpcCBvZiB5b3VyIGNob2ljZS4NCg0KDQoNCg0KIyBTYXZlIGBnZ3Bsb3RgIEltYWdlcyANCg0KQSBgZ2dwbG90YCBjYW4gYmUgc2F2ZWQgdG8gZGlmZmVyZW50IGZpbGUgZm9ybWF0cywgaW5jbHVkaW5nIFBERiwgU1ZHIHZlY3RvciBmaWxlcywgUE5HLCBUSUZGLCBKUEVHLCBldGMuDQoNCldlIGNhbiBlaXRoZXIgcHJpbnQgZGlyZWN0bHkgYSBgZ2dwbG90YCBpbnRvIGBQTkcvUERGYCBmaWxlcyBvciB1c2UgdGhlIGNvbnZlbmllbnQgZnVuY3Rpb24gYGdnc2F2ZSgpYCBmb3Igc2F2aW5nIGEgYGdncGxvdGAuDQoNClRoZSBkZWZhdWx0IG9mIGBnZ3NhdmUoKWAgaXMgdG8gZXhwb3J0IHRoZSBsYXN0IHBsb3QgdGhhdCB5b3UgZGlzcGxheWVkLCB1c2luZyB0aGUgc2l6ZSBvZiB0aGUgY3VycmVudCBncmFwaGljcyBkZXZpY2UuIEl0IGFsc28gZ3Vlc3NlcyB0aGUgdHlwZSBvZiBncmFwaGljcyBkZXZpY2UgZnJvbSB0aGUgZXh0ZW5zaW9uLg0KDQojIyBHZW5lcmFsIFN0ZXBzDQoNClRoZSBzdGFuZGFyZCBwcm9jZWR1cmUgdG8gc2F2ZSBhbnkgZ3JhcGhpY3MgZnJvbSBSIGlzIGFzIGZvbGxvd3M6DQoNCiogT3BlbiBhIGdyYXBoaWMgZGV2aWNlIHVzaW5nIG9uZSBvZiB0aGUgZm9sbG93aW5nIGZ1bmN0aW9uczoNCiAgKyBgcGRmKOKAnHItZ3JhcGhpY3MucGRm4oCdKWAsDQogICsgYHN2ZyjigJxyLWdyYXBoaWNzLnN2Z+KAnSlgLA0KICArIGBwbmco4oCcci1ncmFwaGljcy5wbmfigJ0pYCwNCiAgKyBgdGlmZijigJxyLWdyYXBoaWNzLnRpZmbigJ0pYCwNCiAgKyBganBlZyjigJxyLWdyYXBoaWNzLmpwZ+KAnSlgLCBldGMuDQoNCiogQWRkaXRpb25hbCBhcmd1bWVudHMgaW5kaWNhdGluZyB0aGUgd2lkdGggYW5kIHRoZSBoZWlnaHQgKGluIGluY2hlcykgb2YgdGhlIGdyYXBoaWNzIHJlZ2lvbiBjYW4gYmUgYWxzbyBzcGVjaWZpZWQgaW4gdGhlIG1lbnRpb25lZCBmdW5jdGlvbi4NCg0KKiBDcmVhdGUgYW5kIHByaW50IGEgcGxvdC4gQ2xvc2UgdGhlIGdyYXBoaWMgZGV2aWNlIHVzaW5nIHRoZSBmdW5jdGlvbiBgZGV2Lm9mZigpYC4NCg0KIyMgU2F2ZSBgZ2dwbG90YCBpbnRvIGEgUERGIEZpbGUgDQoNClRoZSBmb2xsb3dpbmcgY29kZSBpbGx1c3RyYXRlcyBob3cgdG8gc2F2ZSBhIGdncGxvdCBpbiBhIGZvbGRlciBpbiBQREYgZm9ybWF0Lg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KIyBzY2F0dGVyIHBsb3RzDQppcmlzLnNjYXR0ZXIgPC0gZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFNlcGFsLldpZHRoKSkgKyANCiAgICAgICAgICAgZ2VvbV9wb2ludCgpDQojIyBib3gtcGxvdA0KaXJpcy5ib3hwbG90IDwtIGdncGxvdChpcmlzLCBhZXMoU3BlY2llcywgU2VwYWwuTGVuZ3RoKSkgKyANCiAgZ2VvbV9ib3hwbG90KCkNCiMgUHJpbnQgcGxvdHMgdG8gYSBQREYgZmlsZTogb25lIHBhZ2UgcGVyIFBERiBmaWxlDQpwZGYoInNhdmVQREZnZ3Bsb3QucGRmIikgICAjIFNhdmUgdGhlIFBERiBmaWxlIGluIGdncGxvdCBmb2xkZXIuDQpwcmludChpcmlzLnNjYXR0ZXIpICAgICAjIFBsb3QgMSAtLT4gaW4gdGhlIGZpcnN0IHBhZ2Ugb2YgUERGDQpwcmludChpcmlzLmJveHBsb3QpICAgICAjIFBsb3QgMiAtLS0+IGluIHRoZSBzZWNvbmQgcGFnZSBvZiB0aGUgUERGDQpkZXYub2ZmKCkgDQpgYGANCg0KIyMgU2F2ZSBnZ3Bsb3Qgd2l0aCBgZ2dzYXZlKClgDQoNCkl04oCZcyBhbHNvIHBvc3NpYmxlIHRvIG1ha2UgYSBnZ3Bsb3QgYW5kIHNhdmUgaXQgZnJvbSB0aGUgc2NyZWVuIHVzaW5nIHRoZSBmdW5jdGlvbiBgZ2dzYXZlKClgLg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KIyAxLiBDcmVhdGUgYSBwbG90OiBkaXNwbGF5ZWQgb24gdGhlIHNjcmVlbiAoYnkgZGVmYXVsdCkNCmdncGxvdChtdGNhcnMsIGFlcyh3dCwgbXBnKSkgKyBnZW9tX3BvaW50KCkNCiMgMi4xLiBTYXZlIHRoZSBwbG90IHRvIGEgcGRmDQpnZ3NhdmUoIm10Y2FybXlwbG90LnBkZiIpDQojIDIuMiBPUiBzYXZlIGl0IHRvIHBuZyBmaWxlDQpnZ3NhdmUoIm10Y2FybXlwbG90LnBuZyIpDQpgYGANCldlIGNhbiBhbHNvIHNhdmUgbXVsdGlwbGUgcGxvdHMgaW4gdGhlIHNhbXBsZSBmb3JtYXQgdG8gYSBzaW5nbGUgZmlsZS4gV2UgY2FuIHVzZSBgcGxvdF9ncmlkKClgIGluICoqe2Nvd3Bsb3R9KiogdG8gbWFrZSB0d28gZmlndXJlcyBvbiB0aGUgc2FtZSBncmFwaGljIHBhZ2UgYW5kIHRoZW4gdXNlIGBnZ3NhdmUoKWAgdG8gc2F2ZSBpdCB0byBhIHNpbmdsZSBmaWxlLg0KDQpgYGB7cn0NCiMgDQpwMSA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICsgZ2VvbV9wb2ludCgpDQpwMiA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMod3QpKSArIGdlb21faGlzdG9ncmFtKCkNCmNvbWJpbmVkUGxvdCA8LSBwbG90X2dyaWQocDEsIHAyLCBsYWJlbHM9YygiQSIsICJCIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gMiwgbnJvdyA9IDEpDQojIw0KZ2dzYXZlKCJDb21iaW5lZFBsb3QucG5nIiwgcGxvdCA9IGNvbWJpbmVkUGxvdCkNCmBgYA0K